Can Hooks and Context replace React-Redux?

Thomas James Byers
Nov 22, 2018 · 3 min read

Dan Abramov’s react-redux library gives us a way to connect our redux store to our react components. It saves us from having to pass props all the way down and gives us some vDom optimizations for free.

The only downside is having to write all those mapStateToProps functions, but now, thanks to two new React features, we may no longer have to.


The new Context API allows us to share state throughout the component hierarchy. This works much like the Provider component from react-redux allowing us to make the store available to the entire component tree.

const MyContext = React.createContext(someInitialValue);<MyContext.Provider value={someValueToProvide}>
<MyApp />
</MyContext.Provider>
...<MyContext.Consumer>
providedValue => <MyComponent someAttribute={providedValue} />
</MyContext.Consumer>

The experimental React Hooks API allows us to write functional components with local state and side effects, and also to access context.

import React, { useState, useEffect, useContext } from 'react';const MyComponent = () => {
const [someValue, update] = useState(someInitialValue);
const providedValue = useContext(MyContext);
useEffect(() => {
someSideEffect();
return someCleanUpFunction; // e.g. unsubscribe
});
return <input type="text" value={someValue} onChange={update} />
};

Combining the built in hooks to create our own custom hooks enables us to extract and share common logic.

import React, { useState, useEffect} from 'react';const useMyHook = defaultValue => {
const [data, update] = useState('');
useEffect(() => {
const callback = res => update(res);
const unsubscribe = getData(callback);
return unsubscribe;
});
return data;
};
const MyComponent = () => {
const data = useMyHook('');
return <div>{data}</div>
};

Of course, the most common logic in our components is for interacting with the store.

We can use the useContext hook to access the store from within a functional component. This means we can set local state with getState and selectors, and subscribe to future updates as a side effect. We can even choose to update local state only when it would be different thereby avoiding unnecessary re-renders. We can also access dispatch for dispatching actions to the store.

Let’s see what it would look like…

import Store from 'hux';const store = createStore(reducer);

const EntryPoint = () => (
<Store.Provider value={store}>
<App />
</Store.Provider>
);

…and in the component…

import { useDispatch, useSelector } from 'hux';

const MyComponent = id => {
const value = useSelector(mySelector, id);
const update = useDispatch(myActionCreator)

return (
<input
type="text"
value={value}
onChange={e => update(e.target.value)}>
);
}

…and this is where the magic happens…

import React, {useContext, useEffect, useState} from 'react';
import { compose } from 'redux';
const Store = React.createContext();export const useDispatch = actionCreator => {
const { dispatch } = useContext(Store);
return compose(dispatch, actionCreator);
};
export const useSelector = (selector, ...rest) => {
const { subscribe, getState } = useContext(Store);
const select = () => selector(
getState(),
...rest
);
const initial = select();
const [value, update] = useState(initial);
const listener = () => {
const next = select()
if (next !== value) {
update(next);
}
};
useEffect(() => subscribe(listener)); return value;
}
export default Store;

Here we have defined two custom hooks.

The first, useDipatch, uses the useContext hook internally to get the store’s dispatch method then composes it with an action creator passed in from the component. The function it returns will take n arguments, pass them to the action creator and then dispatch the result.

The second hook is a little more complicated. We use the useState hook to store the value we get from passing the current state to the selector. Next we create a side effect using the useEffect hook. Our side effect is to subscribe to the redux store and then update local state whenever the result of our selector changes. The subscribe method returns an implicit unsubscribe which is used by the React Hooks API to clean up afterwards.

Finally we return the resulting value for use in the component. Note how we only update the value returned from useSelector if it is not strictly equal to the previous value. This will prevent components from re-rendering when nothing has changed.

Want this as a library? Get it from npm!

Thomas James Byers

Written by

Javascript Developer — Manchester

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade