Global state with React hooks
November 15, 2019
React hooks open up a world of possibilites with functional components. As well as the standard hooks from the React guys, you can also create your own hooks to help manage stateful logic inside otherwise stateless components.
An alternative to useState. Accepts a reducer of type (state, action) => newState, and returns the current state paired with a dispatch method.(If you’re familiar with Redux, you already know how this works.)
useReducer is usually preferable to useState when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one.useReducer also lets you optimize performance for components that trigger deep updates because you can pass dispatch down instead of callbacks.
If you have played with useState, the first hook most people come accross, useReducer is just a way to manage more complex state.
Here is an example of simple useState:
This hook returns an array which is destructured, this array contains our state object which holds our data and a method we can use to update our state.
Imagine you have a form component with loads of fields, this approach can quickly become unweildly if you have multiple state objects, one for each field on your form.
This is where useReducer comes in. Take a look at this example:
So as you can see this hook differs slightly. You again have a destructured array returned, this time you have your state object and a dispatch method to call your reducer function. You pass in 2 things to this hook. The first, a reducer function (code that is run when you want to update your state) you will notice this function takes in 2 arguments state and action - more on this later. The second argument of the hook, is an initial value. This is similar to the initial value in useState but you can use complex objects here, not just primitive types.
To give you a more real-world example of this, let’s go back to our form example:
This is neat, but we are still confined to within a single functional component here. We want to find a way to be able to “hook into” this from anywhere in our app’s component tree and access the state object and dispatch method.
Accepts a context object (the value returned from React.createContext) and returns the current context value for that context. The current context value is determined by the value prop of the nearest <MyContext. Provider> above the calling component in the tree.
When the nearest <MyContext. Provider> above the component updates, this Hook will trigger a rerender with the latest context value passed to that MyContext provider.
This might not mean a lot to you as it took me some time to get my head around this one.
I will show you how it works.
You create a context in React like this:
The null passed in here is the initial value, this can be anything you like.
To “provide” this context, or to open this up to be “consumed” by other components you need to wrap your app in a provider for your context. There are a couple of ways/places you could do this but if you want this to be wrapped around your whole app, I prefer this method (in the index file for your app):
This AppContextProvider that you are wrapping around your app is a separate component which may look like this:
As you can see this wraps the components children (in our example the App component which in turn contains all the components that make our app) with this provider which has a value.
We are setting that value to be an object containing our state object and dispatch method meaning we can access these anywhere in our component tree.
No more passing props down multiple children just to use something many levels deeper.
Instead we can access it like this:
For a more complete example of this is a real world app you can checkout an implementation of a simple, classic memory game I created using these hooks.
Sean Barnard, software developer at Innovyze.