use_reducer Hook
Here is where things get interesting. The use_reducer
hook is used to manage state in a functional way.
It is similar to the use_state
hook but allows us to manage more complex state.
Basic use
Using the use_reducer
hook is very similar to reducer concepts in other frameworks. The hook takes in a reducer
function and an initial state.
Intern, the hook returns a tuple containing the current state and a dispatch function. The dispatch function is used to
dispatch actions to the reducer function.
from hooks import use_reducer
def tasks_reducer(current_state: dict, action: dict) -> dict:
if action["type"] == "ADD_TASK":
return {"tasks": current_state["tasks"] + [action["task"]]}
return current_state
state, dispatch = use_reducer(tasks_reducer, {"tasks": []})
new_state = dispatch({"type": "ADD_TASK", "task": "Do the dishes"})
print(state) # Output: {"tasks": []}
print(new_state) # Output: {"tasks": ["Do the dishes"]}
The dispatch function can be used to dispatch actions from anywhere in the application. This allows us to manage the state in a centralised location. In addition, the use_reducer hook can be used multiple times in the same application and even in the same function. It knows to identify the correct state to update based on the reducer function passed in.
Accessing the state in separate functions
You may use the use_reducer
hook in separate functions and still access the same state. This is because the hook
uses the same reducer function to identify the state to update. This is useful when you want to separate your logic
into different functions.
from hooks import use_reducer
def tasks_reducer(current_state: dict, action: dict) -> dict:
if action["type"] == "ADD_TASK":
return {"tasks": current_state["tasks"] + [action["task"]]}
return current_state
def add_task(task: str):
state, dispatch = use_reducer(tasks_reducer, {"tasks": []})
dispatch({"type": "ADD_TASK", "task": task})
def get_tasks():
state, dispatch = use_reducer(tasks_reducer)
return state["tasks"]
add_task("Do the dishes")
add_task("Do the laundry")
print(get_tasks()) # Output: ["Do the dishes", "Do the laundry"]
Middleware
The use_reducer
hook also supports middleware. Middleware is a function that is called before the reducer processes
the action. This allows you to perform actions such as logging or analytics before the reducer processes the action.
from hooks import use_reducer
def logging_middleware(state: dict, process: Callable, action: dict) -> dict:
print("Action: ", action)
new_state: dict = process(state, action)
print("New state: ", new_state)
return new_state
def tasks_reducer(current_state: dict, action: dict) -> dict:
if action["type"] == "ADD_TASK":
return {"tasks": current_state["tasks"] + [action["task"]]}
return current_state
state, dispatch = use_reducer(tasks_reducer, {"tasks": []}, [logging_middleware])
dispatch({"type": "ADD_TASK", "task": "Do the dishes"})
# Output: Action: {"type": "ADD_TASK", "task": "Do the dishes"}
# New state: {"tasks": ["Do the dishes"]}
You may add as many middleware functions as you like. The middleware functions are called in the order they are provided.
Combining reducers
You may also combine multiple reducers into one using the combine_reducers
function. This is useful when you want to
split your state into multiple reducers.
from hooks import use_reducer, combine_reducers
def tasks_reducer(current_state: dict, action: dict) -> dict:
if action["type"] == "ADD_TASK":
return {"tasks": current_state["tasks"] + [action["task"]]}
return current_state
def user_reducer(current_state: dict, action: dict) -> dict:
if action["type"] == "SET_USER":
return {"user": action["user"]}
return current_state
combined_reducer = combine_reducers(tasks_reducer, user_reducer)
state, dispatch = use_reducer(combined_reducer, {"tasks": [], "user": None})
dispatch({"type": "ADD_TASK", "task": "Do the dishes"})
new_state = dispatch({"type": "SET_USER", "user": "John Doe"})
print(new_state) # Output: {"tasks": ["Do the dishes"], "user": "John Doe"}
The combine_reducers
function takes in multiple reducer functions and returns a single reducer function. The
returned reducer function will call each reducer function with the current state and action. The returned reducer
function will then combine the results of each reducer function into a single state.
Next steps
Learn about creating custom hooks.
Learn about scoping hooks with hooks_scope decorator.