Alright, on to part 2! In part 1 we covered how to use React Context API to pass values throughout our application.
In part 2 we will see how to use useReducer hook to maintain our state throughout our application. And using React Context API to pass that state to our entire application.
We start by adding useReducer to our ProductsProvider component.
//products_context.js
import React, {useReducer} from "react";
import reducer from "../products_reducer";
export const ProductsProvider = ({ children }) => {
const initialState = {
productsLoading: false,
products: [],
};
const [state, dispatch] = useReducer(reducer, initialState);
return (
<ProductContext.Provider value={}>
{children}
</ProductContext.Provider>
);
};
To explain the parts of useReducer , we have state which is the current state , dispatch which takes in type and payload where type tells the reducer what actions to take and payload is data to be passed to the reducer, we have reducer which is a function that we create that decides how to modify the state based on our dispatch type , and lastly we have initialState , which is self-explanatory.
With that , lets define our reducer function
//products_reducer.js
const products_reducer = (state, action) => {
if (action.type === "GET_PRODUCTS_BEGIN") {
return { ...state, productsLoading: true };
}
if (action.type === "GET_PRODUCTS_SUCCESS") {
return {
...state,
productsLoading: false,
products: action.payload,
};
}
};
export default products_reducer;
Our reducer function takes in 2 arguments , the currentState and the action , essentially your action is the dispatch. The reducer checks the type of action and returns an updated state based on the action type.
So how do we use dispatch to provide the right type so that the reducer can update the state? Lets hop back to products_context.js , into our ProductsProvider function where we defined our useReducer.
//products_context.js
export const ProductsProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initialState);
const fetchProducts = async () => {
dispatch({ type: GET_PRODUCTS_BEGIN });
let response = {};
try {
response = await axios.get(url);
let products = response.data;
dispatch({ type: GET_PRODUCTS_SUCCESS, payload: products })
} catch (error) {
console.log({ error });
}
};
return (
<ProductContext.Provider value={{ ...state , fetchProducts }}>
{children}
</ProductContext.Provider>
);
};
In our fetchProducts function , we use the dispatch function to modify our state , notice that in the second dispatch we pass the response from our API call as a payload to the dispatch. This will in turn use that payload value in the reducer function with the type of "GET_PRODUCTS_SUCCESS".
Lastly , we spread our state into the value prop of the ProductContext.Provider , so whenever there is an update in our state, the components that use this state value will be re-rendered.
//products_context.js
<ProductContext.Provider value={{ ...state , fetchProducts }}>
{children}
</ProductContext.Provider>
Here in our Product display page , we can destructure our state values and use them accordingly to display the data.
//Product.js Page
import { useProductContext } from "../products_context";
const { products , productsLoading , fetchProducts } = useProductContext();
With that we come to the end our our React Context API and useReducer series. I hope you've learned something from this post. Do share in the comments below how you handle states in your application.
Top comments (0)