Reactive Props
Legend-State makes it easy to bind props directly to an observable or selector, so the component re-renders itself whenever the observable or selector changes without needing to re-render the parent or children.
Legend-State provides reactive versions of all intrinsic components. For every prop it adds another one ending with $
that accepts a selector (an observable or a function returning a value based on observables), and it automatically tracks it for changes and re-renders it.
Note: The reactive components are created on-demand because the Legend object is a Proxy, so you don't need to worry about a performance hit of creating every wrapped component upfront.
React
import { enableLegendStateReact, useObservable } from "@legendapp/state/react"
import { Legend } from "@legendapp/state/react-components"
enableLegendStateReact();
function Component() {
const state = useObservable({ name: '', age: 18 })
return (
<div>
// Reactive styling
<Legend.div
style$={() => ({
color: state.age.get() > 5 ? 'green' : 'red'
})}
className$={() => state.age.get() > 5 ? 'kid' : 'baby'}
/>
// Reactive children
<Legend.div children$={() => (
<div>{state.age.get() > 5 ? <Kid /> : <Baby />}
)} />
// Two-way bind to inputs
<Legend.textarea value$={state.name} />
<Legend.select value$={state.age}>...</Legend.select>
<Legend.input
value$={state.name}
className$={(value) => !value && "border-red-500"}
style$={(value) => !value && { borderWidth: 1 }}
/>
</div>
)
}
React Native
import { View } from "react-native"
import { enableLegendStateReact, useObservable } from "@legendapp/state/react"
import { Legend } from "@legendapp/state/react-native-components"
enableLegendStateReact();
function Component() {
const state = useObservable({ name: '', enabled: false })
return (
<View>
// Reactive styling
<Legend.Text
style$={() => ({
color: state.enabled.get() ? 'green' : 'red'
})}
>
{state.name}
</Legend.Text>
// Two-way bind to inputs
<Legend.Switch value$={state.enabled} />
<Legend.TextInput
value$={state.name}
style$={() => (state.name.get() === 'Legend' && { backgroundColor: 'green' })}
/>
</View>
)
}
Two-Way Binding to Inputs
If you pass an observable to the value$
prop of an input component, it sets up a two way binding that updates the observable automatically, so you don't have to worry about managing refs or onChange handlers.
import { enableLegendStateReact, useObservable } from "@legendapp/state/react"
import { Legend } from "@legendapp/state/react-components"
enableLegendStateReact();
function Component() {
const state = useObservable({ name: 'Legend' })
return (
<div>
<div>{state.name}</div>
<Legend.input
value$={state.name}
className$={() =>
state.name.get() === 'Legend' ?
'text-white' :
'text-red-500'
}
/>
</div>
)
}
Make external components reactive
You can wrap external components in reactive
to add reactive versions of all of their props.
In this example we make a Framer Motion component reactive so that we can update its animations based on observables without needing to re-render the parent component or its children.
import { reactive } from "@legendapp/state/react"
import { motion } from "framer-motion"
const ReactiveMotionDiv = reactive(motion.div);
function Component() {
const width = useObservable(100)
return (
<ReactiveMotionDiv
animate$={() => ({
x: width
})}
>
...
</ReactiveMotionDiv>
)
}
Make your own
You can just wrap your components in reactive
if you'd like, but it's also easy to make specific parameters reactive. All it needs is a useSelector
to make the prop reactive and get the raw value.
import { useSelector } from "@legendapp/state/react"
function MyComponent({ prop, prop$ }) {
// Get the observable value if it exists or
// otherwise get the normal prop value
prop = useSelector(prop$ ?? prop)
...
}
See react-components on GitHub for the latest full source.