recoil
State management library from Facebook
Will it kill redux?
14/05/2020 - React europe
Dave McCabe aka @mcc_abe
State management
State management strategies
- Local state
- Context
- Redux
- Mobex
- ReactQuery
- +XXX number of libs
HOw is recoil different?
ATOMS
ATOMS
Selectors
ATOMS
Selectors
Components
CODE
const App = () => {
return (
<div>
<Counter />
</div>
)
}
const Counter = () => {
const [value, setValue] = useState(0)
return (
<>
<div>{value}</div>
<button onClick={() => setValue(prev => prev + 1)}>+</button>
</>
)
}
const App = () => {
return (
<div>
<Counter />
<ByTwo />
</div>
)
}
const Counter = () => {
const [value, setValue] = useState(0)
return (
<>
<div>{value}</div>
<button onClick={() => setValue(prev => prev + 1)}>+</button>
</>
)
}
const ByTwo = ({ value }) => <div>{value * 2}</div>
const App = () => {
const [value, setValue] = useState(0)
return (
<div>
<Counter value={value} setValue={setValue} />
<ByTwo value={value} />
</div>
)
}
const Counter = ({ value, setValue }) => {
return (
<>
<div>{value}</div>
<button onClick={() => setValue(prev => prev + 1)}>+</button>
</>
)
}
const ByTwo = ({ value }) => <div>{value * 2}</div>
const App = () => {
const [value, setValue] = useState(0)
return (
<div>
<Counter value={value} setValue={setValue} />
<ByTwo value={value} />
<VeryExpensiveSubTreeToReRender>???</VeryExpensiveSubTreeToReRender>
</div>
)
}
const Counter = ({ value, setValue }) => {
return (
<>
<div>{value}</div>
<button onClick={() => setValue(prev => prev + 1)}>+</button>
</>
)
}
const ByTwo = ({ value }) => <div>{value * 2}</div>
const Ctx = React.createContext()
const App = () => {
const [value, setValue] = useState(0)
return (
<Ctx.Provider value={{ value, setValue }}>
<div>
<VeryExpensiveSubTreeToReRender>
<Counter />
<ByTwo />
</VeryExpensiveSubTreeToReRender>
</div>
</Ctx.Provider>
)
}
const Counter = () => {
const { value, setValue } = useContext(Ctx)
return (
<>
<div>{value}</div>
<button onClick={() => setValue(prev => prev + 1)}>+</button>
</>
)
}
const ByTwo = () => {
const { value } = useContext(Ctx)
return <div>{value * 2}</div>
}
const Ctx = React.createContext()
const VeryExpensiveSubTreeToReRender = React.memo(ExpensiveSubTree);
const App = () => {
const [value, setValue] = useState(0)
return (
<Ctx.Provider value={{ value, setValue }}>
<div>
<VeryExpensiveSubTreeToReRender>
<Counter />
<ByTwo />
</VeryExpensiveSubTreeToReRender>
</div>
</Ctx.Provider>
)
}
const Counter = () => {
const { value, setValue } = useContext(Ctx)
return (
<>
<div>{value}</div>
<button onClick={() => setValue(prev => prev + 1)}>+</button>
</>
)
}
const ByTwo = () => {
const { value } = useContext(Ctx)
return <div>{value * 2}</div>
}
// const reducer = () => ...
const store = createStore({ reducer })
const App = () => {
return (
<Provider store={store}>
<div>
<Counter />
<ByTwo />
<VeryExpensiveSubTreeToReRender>???</VeryExpensiveSubTreeToReRender>
</div>
</Provider>
)
}
const Counter = () => {
const value = useSelector((state) => state.value);
const dispatch = useDispatch();
return (
<>
<div>{value}</div>
<button onClick={() => dispatch(setValue(value + 1))}>+</button>
</>
)
}
const ByTwo = () => {
const value = useSelector((state) => state.value);
return <div>{value * 2}</div>
}
const App = () => {
return (
<div>
<Counter />
<ByTwo />
</div>
)
}
const Counter = () => {
const [value, setValue] = useState(0)
return (
<>
<div>{value}</div>
<button onClick={() => setValue(prev => prev + 1)}>+</button>
</>
)
}
const ByTwo = ({ value }) => <div>{value * 2}</div>
import { RecoilRoot } from "recoil"
const App = () => {
return (
<RecoilRoot>
<div>
<Counter />
<ByTwo />
</div>
</RecoilRoot>
)
}
const Counter = () => {
const [value, setValue] = useState(0)
return (
<>
<div>{value}</div>
<button onClick={() => setValue(prev => prev + 1)}>+</button>
</>
)
}
const ByTwo = ({ value }) => <div>{value * 2}</div>
import { RecoilRoot, atom } from "recoil"
const counter = atom({
key: "counter",
default: 0
})
const App = () => {
return (
<RecoilRoot>
<div>
<Counter />
<ByTwo />
</div>
</RecoilRoot>
)
}
const Counter = () => {
const [value, setValue] = useState(0)
return (
<>
<div>{value}</div>
<button onClick={() => setValue(prev => prev + 1)}>+</button>
</>
)
}
const ByTwo = ({ value }) => <div>{value * 2}</div>
import { RecoilRoot, atom, useRecoilState } from "recoil"
const counter = atom({
key: "counter",
default: 0
})
const App = () => {
return (
<RecoilRoot>
<div>
<Counter />
<ByTwo />
</div>
</RecoilRoot>
)
}
const Counter = () => {
const [value, setValue] = useRecoilState(counter)
return (
<>
<div>{value}</div>
<button onClick={() => setValue(prev => prev + 1)}>+</button>
</>
)
}
const ByTwo = ({ value }) => <div>{value * 2}</div>
import { RecoilRoot, atom, useRecoilState, useRecoilValue } from "recoil"
const counter = atom({
key: "counter",
default: 0
})
const App = () => {
return (
<RecoilRoot>
<div>
<Counter />
<ByTwo />
</div>
</RecoilRoot>
)
}
const Counter = () => {
const [value, setValue] = useRecoilState(counter)
return (
<>
<div>{value}</div>
<button onClick={() => setValue(prev => prev + 1)}>+</button>
</>
)
}
const ByTwo = () => {
const value = useRecoilValue(counter);
return <div>{value * 2}</div>
}
import { RecoilRoot, atom, useRecoilState, useRecoilValue, selector } from "recoil"
const counter = atom({
key: "counter",
default: 0
})
const byTwo = selector({
key: "byTwo",
get: ({ get }) => {
const counterValue = get(counter);
return counterValue * 2
}
})
// App
// Counter
const ByTwo = () => {
const value = useRecoilValue(counter);
return <div>{value * 2}</div>
}
import { RecoilRoot, atom, useRecoilState, useRecoilValue, selector } from "recoil"
const counter = atom({
key: "counter",
default: 0
})
const byTwo = selector({
key: "byTwo",
get: ({ get }) => {
const counterValue = get(counter);
return counterValue * 2
}
})
// App
// Counter
const ByTwo = () => {
const value = useRecoilValue(byTwo);
return <div>{value}</div>
}
Async
const currentUserNameQuery = selector({
key: 'CurrentUserName',
get: async ({get}) => {
const response = await myDBQuery({
userID: get(currentUserIDState),
});
return response.name;
},
});
function CurrentUserInfo() {
const userName = useRecoilValue(currentUserNameQuery);
return <div>{userName}</div>;
}
function MyApp() {
return (
<RecoilRoot>
<React.Suspense fallback={<div>Loading...</div>}>
<CurrentUserInfo />
</React.Suspense>
</RecoilRoot>
);
}
Is it production ready?
Will it kill redux?
Arguments against redux
- Learning curve
- Too much boilerplate
- Not React libraries (concurrent mode?)
- 3rd party libs for cache/async
Is it worth to check?
Is it worth to observe the recoil?
Pros
- More Reactish (useState -> useRecoilState)
- Easier learning curve
- Performance
- Concurrent mode support (soon)
cons
- React only (hooks only)
- Complicated Async API
- Not production ready - yet...
Thank you
recoil
By Przemek Suchodolski
recoil
- 270