Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
import React, { useState, useEffect, useReducer, useContext, useRef, createContext } from "react";
// Hook definition.
export const useContextAndReducer = (reducer, context) => {
const ctx = useContext(context);
const [state, dispatch] = useReducer(reducer, ctx.state);
const { setState } = ctx;
// Update the context when the state changes from the reducer.
useEffect(() => {
setState({ ...state });
}, [state, setState]);
// Return the state from the reducer & the dispatch function.
return [state, dispatch];
};
// Create the context so that state can persist
// across many components & across different pages,
// etc.
const Context = createContext();
// Create the context provider so that other
// components can consume the shared state.
const ContextProvider = (props) => {
const { state, setState } = props;
return (
<Context.Provider value={{ state, setState }}>
{props.children}
</Context.Provider>
);
};
// Create your reducer, this is a simple
// example.
const todoReducer = (state, action) => {
switch (action.type) {
case "ADD":
return { ...state, todos: [...state.todos, action.payload] };
case "REMOVE":
const todos = [...state.todos];
todos.splice(action.payload, 1);
return { ...state, todos };
case "EMPTY":
return { ...state, todos: [] };
default:
return state;
}
};
// The business logic to adding a todo item to the list.
const handleAddTodo = (dispatch) => (ref) => () => {
dispatch({ type: "ADD", payload: ref.current.value });
ref.current.value = "";
};
// The business logic to handle removing an item from the todo list.
const handleRemoveTodo = (dispatch) => (index) => () => {
dispatch({ type: "REMOVE", payload: index });
};
// The business logic to empty the todo list.
const handleEmptyTodoList = (dispatch) => () => {
dispatch({ type: "EMPTY" });
};
// Usage
// This is some component for the sake of example.
const TodoListComponent = () => {
const ref = useRef(null);
const [state, dispatch] = useContextAndReducer(todoReducer, Context);
return (
<>
<input type="text" ref={ref} />
<button onClick={handleAddTodo(dispatch)(ref)}>Add</button>
<button onClick={handleEmptyTodoList(dispatch)}>Empty</button>
<ul>
{state.todos.map((todo, index) => {
return (
<li key={index}>
<p>{todo}</p>
<button onClick={handleRemoveTodo(dispatch)(index)}>
Remove
</button>
</li>
);
})}
</ul>
</>
);
};
// This is essentially the parent/root/container component.
function App() {
const [state, setState] = useState({
todos: ["Go shopping", "Hoover the house", "Cook dinner"],
});
return (
<ContextProvider state={state} setState={setState}>
<TodoListComponent />
</ContextProvider>
);
}
export default App;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment