# Unstated Next > 200 bytes to never think about React state management libraries ever again - **React Hooks** _use them for all your state management._ - **~200 bytes** _min+gz._ - **Familiar API** _just use React as intended._ - **Minimal API** _it takes 5 minutes to learn._ - **Written in TypeScript** _and will make it easier for you to type your React code._ But, the most important question: Is this better than Redux? Well... - **It's smaller.** _It's 40x smaller._ - **It's faster.** _Componentize the problem of performance._ - **It's easier to learn.** _You already will have to know React Hooks & Context, just use them, they rock._ - **It's easier to integrate.** _Integrate one component at a time, and easily integrate with every React library._ - **It's easier to test.** _Testing reducers is a waste of your time, make it easier to test your React components._ - **It's easier to typecheck.** _Designed to make most of your types inferable._ - **It's minimal.** _It's just React._ So you decide. ## Install ```sh npm install --save unstated-next ``` ## Example ```js import React, { useState } from "react" import { createContainer } from "unstated-next" import { render } from "react-dom" function useCounter() { let [count, setCount] = useState(0) let decrement = () => setCount(count - 1) let increment = () => setCount(count - 1) return { count, decrement, increment } } let Counter = createContainer(useCounter) function CounterDisplay() { let counter = Counter.useContainer() return (
{counter.count}
) } function App() { return ( ) } render(, document.getElementById("root")) ``` ## API ### `createContainer(useHook)` ```js import { createContainer } from "unstated-next" function useCustomHook() { let [value, setInput] = useState() let onChange = e => setValue(e.currentTarget.value) return { value, onChange } } let Container = createContainer(useCustomHook) // Container === { Provider, useContainer } ``` ### `` ```js function ParentComponent() { return ( ) } ``` ### `Container.useContainer()` ```js function ChildComponent() { let input = Container.useContainer() return } ``` ### `useContainer(Container)` ```js import { useContainer } from "unstated-next" function ChildComponent() { let input = useContainer(Container) return } ``` ## Guide If you've never used React Hooks before, I recommend pausing and going to read through [the excellent docs on the React site](https://reactjs.org/docs/hooks-intro.html). So with hooks you might create a component like this: ```js function CounterDisplay() { let [count, setCount] = useState(0) let decrement = () => setCount(count - 1) let increment = () => setCount(count + 1) return (

You clicked {count} times

) } ``` Then if you want to share the logic behind the component, you could pull it out into a custom hook: ```js function useCounter() { let [count, setCount] = useState(0) let decrement = () => setCount(count - 1) let increment = () => setCount(count + 1) return { count, decrement, increment } } function CounterDisplay() { let counter = useCounter() return (

You clicked {counter.count} times

) } ``` But what if you want to share the state in addition to the logic, what do you do? This is where context comes into play: ```js function useCounter() { let [count, setCount] = useState(0) let decrement = () => setCount(count - 1) let increment = () => setCount(count + 1) return { count, decrement, increment } } let Counter = createContext(null) function CounterDisplay() { let counter = useContext(Counter) return (

You clicked {counter.count} times

) } function App() { let counter = useCounter() return ( ) } ``` This is great, it's perfect, more people should write code like this. But sometimes we all need a little bit more structure and intentional API design in order to get it consistently right. By introducing the `createContainer()` function, you can think about your custom hooks as "containers" and have an API that's clear and prevents you from using it wrong. ```js import { createContainer } from "unstated-next" function useCounter() { let [count, setCount] = useState(0) let decrement = () => setCount(count - 1) let increment = () => setCount(count + 1) return { count, decrement, increment } } let Counter = createContainer(useCounter) function CounterDisplay() { let counter = Counter.useContainer() return (

You clicked {counter.count} times

) } function App() { return ( ) } ``` Here's the diff of that change: ```diff - import { createContext, useContext } from "react" + import { createContainer } from "unstated-next" function useCounter() { ... } - let Counter = createContext(null) + let Counter = createContainer(useCounter) function CounterDisplay() { - let counter = useContext(Counter) + let counter = Counter.useContainer() return (
...
) } function App() { - let counter = useCounter() return ( - + ) } ``` If you're using TypeScript (which I encourage you to learn more about if you are not), this also has the benefit of making TypeScript's built-in inference work better. As long as your custom hook is typed, then everything else will just work. ## Tips ### Tip #1: Composing Containers Because we're just working with custom React hooks, we can compose containers inside of other hooks. ```js function useCounter() { let [count, setCount] = useState(0) let decrement = () => setCount(count - 1) let increment = () => setCount(count + 1) return { count, decrement, increment, setCount } } let Counter = createContainer(useCounter) function useResettableCounter() { let counter = Counter.useContainer() let reset = () => counter.setCount(0) return { ...counter, reset } } ``` ### Tip #2: Keeping Containers Small This can be useful for keeping your containers small and focused. Which can be important if you want to code split the logic in your containers: Just move them to their own hooks and keep just the state in containers. ```js function useCount() { return useState(0) } let Count = createContainer(useCount) function useCounter() { let [count, setCount] = Count.useContainer() let decrement = () => setCount(count - 1) let increment = () => setCount(count + 1) let reset = () => setCount(0) return { count, decrement, increment, reset } } ``` ### Tip #3: Optimizing components There's no "optimizing" `unstated-next` to be done, all of the optimizations you might do would be standard React optimizations. #### 1) Optimizing expensive sub-trees by splitting the component apart **Before:** ```js function CounterDisplay() { let counter = Counter.useContainer() return (

You clicked {counter.count} times

SUPER EXPENSIVE RENDERING STUFF
) } ``` **After:** ```js function ExpensiveComponent() { return (
SUPER EXPENSIVE RENDERING STUFF
) } function CounterDisplay() { let counter = Counter.useContainer() return (

You clicked {counter.count} times

) } ``` #### 2) Optimizing expensive operations with useMemo() **Before:** ```js function CounterDisplay(props) { let counter = Counter.useContainer() // Recalculating this every time `counter` changes is expensive let expensiveValue = expensiveComputation(props.input) return (

You clicked {counter.count} times

) } ``` **After:** ```js function CounterDisplay(props) { let counter = Counter.useContainer() // Only recalculate this value when its inputs have changed let expensiveValue = useMemo(() => { return expensiveComputation(props.input) }, [props.input]) return (

You clicked {counter.count} times

) } ``` #### 3) Reducing re-renders using React.memo() and useCallback() **Before:** ```js function useCounter() { let [count, setCount] = useState(0) let decrement = () => setCount(count - 1) let increment = () => setCount(count + 1) return { count, decrement, increment } } let Counter = createContainer(useCounter) function CounterDisplay(props) { let counter = Counter.useContainer() return (

You clicked {counter.count} times

) } ``` **After:** ```js function useCounter() { let [count, setCount] = useState(0) let decrement = useCallback(() => setCount(count - 1), [count]) let increment = useCallback(() => setCount(count + 1), [count]) return { count, decrement, increment } } let Counter = createContainer(useCounter) let CounterDisplayInner = React.memo(props => { return (

You clicked {props.count} times

) }) function CounterDisplay(props) { let counter = Counter.useContainer() return } ```