Build a Simple Redux from Scratch and Integrate It with React
This article explains Redux’s core concepts, demonstrates how to implement a basic createStore function in JavaScript, and shows how to integrate it with React using a custom Provider and connect higher‑order component for state sharing across components.
Redux is a popular state management tool that stores the entire application state in a single place for centralized management. This article implements a simple Redux from scratch.
Core concepts of Redux
The entire application state is stored in a single store.
The only way to change state is by dispatching actions.
Reducers describe how actions transform the state.
Store API
getState() – retrieve the current state.
dispatch(action) – update the state.
subscribe(listener) – register a listener.
The function returned by subscribe can unsubscribe the listener.
Implementing createStore
const createStore = (reducer, preloadedState) => {
let state = preloadedState;
const listeners = new Set();
// dispatch(action) updates state
const dispatch = (action) => {
state = reducer(state, action);
listeners.forEach(listener => listener());
};
// getState() returns state
const getState = () => state;
// subscribe registers a listener
const subscribe = (listener) => {
listeners.add(listener);
return unsubscribe(listener);
};
// unsubscribe removes a listener
const unsubscribe = (listener) => () => {
listeners.delete(listener);
};
return { dispatch, subscribe, getState };
};Using the store
const reducer = (state = { count: 0 }, action) => {
switch (action.type) {
case "increment":
return { count: state.count + 1 };
case "decrement":
return { count: state.count - 1 };
default:
throw new Error();
}
};
const store = createStore(reducer);
const unsubscribe = store.subscribe(() => {
const state = store.getState();
console.log(state.count);
});
store.dispatch({ type: "increment" });
unsubscribe();Integrating with React
The store can be used in React components; when the store changes, components are forced to re‑render.
const Counter = () => {
const [, forceUpdate] = React.useState();
useEffect(() => {
const unsubscribe = store.subscribe(() => {
forceUpdate({});
});
return () => {
unsubscribe();
};
}, []);
return (
<div onClick={() => store.dispatch({ type: "increment" })}>
{store.getState()?.count || 0}
</div>
);
};Simple React‑Redux implementation
First, define a Provider component using React Context to share the store.
const ReduxContext = React.createContext();
const Provider = ({ store, children }) => {
const contextValue = useMemo(() => ({ store }), [store]);
return (
<ReduxContext.Provider value={contextValue}>
{children}
</ReduxContext.Provider>
);
};Then, create a connect higher‑order component that maps store state and dispatch to component props and forces a re‑render when the store updates.
const connect = (mapStateToProps, mapDispatchToProps) => {
return (Component) => {
return (props) => {
const [, forceUpdate] = useState({});
const { store } = useContext(ReduxContext);
const stateToProps = mapStateToProps(store.getState());
const dispatchToProps = mapDispatchToProps(store.dispatch, props);
useEffect(() => {
const unsubscribe = store.subscribe(() => {
forceUpdate({});
});
return () => {
unsubscribe();
};
}, [store]);
return <Component {...props} {...stateToProps} {...dispatchToProps} />;
};
};
};Usage example:
const Counter = (props) => (
<div onClick={props.onClick}>{props.count}</div>
);
const CounterWrap = connect(
state => ({ count: state.count }),
dispatch => ({
onClick: () => {
dispatch({ type: "increment" });
}
})
)(Counter);
const App = () => (
<Provider store={store}>
<CounterWrap />
</Provider>
);How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
