Most of the time when we fetch data, we do that from an external endpoint (API) which is a server. Once that data is fetched, we do a CRUD (Create, Read, Update, Delete) operation on it.
This article will demonstrate how to correctly fetch data from a JSON file in your React & Redux app and consume it on the frontend, while also explaining the workflow of React-Redux.
We are going to create a badge to award users who have received more than 5 votes. We would mock the JSON data locally and give every user that has more than five-votes
a badge using react and redux to consume the data.
Before going further I presume that you have basic knowledge of React, React-Redux & JSON.
In a blank Create React App project, create a local JSON file named data.json
inside the public directory.
Your Fetch API calls made from a React component always look for files or any other relevant assets inside this public directory. Create-React-App
doesn't put your assets automatically inside this directory during compilation so you have to do this manually.
Next, put some dummy JSON data inside this file. For demonstration purposes, the JSON data used in the example below is generated from JSON Generator. If you are using your own JSON, ensure that it is correctly formatted.
[
{
"id": 1,
"name": "Daniel",
"email": "[email protected]",
"post": "I love football",
"votes": 5
}
]
We would need to install some dependencies, which we would be using throughout the project.
To install that open your project directory in your terminal and write the following code:
npm i -s react-redux redux axios redux-thunk
or
yarn add react-redux redux axios
Also, we would install fontaweasome
, where we would import icons as a badge.
npm i --save @fortawesome/fontawesome-svg-core
npm install --save @fortawesome/free-solid-svg-icons
npm install --save @fortawesome/react-fontawesome
or
yarn add @fortawesome/fontawesome-svg-core
yarn add @fortawesome/free-solid-svg-icons
yarn add @fortawesome/react-fontawesome
In the src/ directory let's make 6 Files
Inside constant.js, we need to add a state for fetching user:
const USER = {
LOAD: "REQUEST_USERS_DATA",
LOAD_SUCCESS: "RECEIVE_USERS_DATA",
};
export default USER;
We are going to import the constant in the reducer. A reducer is a pure function that takes an action and the initial state of the application and returns the new state.
The action describes what happened and it is the reducer's job to return to the new state based on that action.
import USER from "./constants";
const initalState = {
usersData: [],
isLoading: false,
isError: false,
};
const reducer = (state = initalState, action) => {
switch (action.type) {
case USER.LOAD:
return {
...state,
isLoading: true,
isError: false,
};
case USER.LOAD_SUCCESS:
return {
...state,
usersData: action.usersData,
isLoading: false,
};
default:
return state;
}
};
export default reducer;
This action file carries a payload of information from your application to store.
Redux relies on actions that get dispatched and listened to by reducers, which update the state accordingly.
import axios from "axios";
import USER from "./constants";
export const requestUsers = (data) => async (dispatch) => {
dispatch({
type: USER.LOAD,
});
try {
const json = await axios.get("data.json");
console.log(json);
dispatch({
type: USER.LOAD_SUCCESS,
usersData: json.data,
isError: false,
});
} catch (e) {
dispatch({
type: USER.LOAD_SUCCESS,
usersData: [],
isError: true,
});
}
};
In the index file we would import fontawesome
so any components could inherit from it.
We would also need a provider and a store. The Provider component makes the Redux store available to any nested components that need to access the Redux store.
Redux stores are global states that store data you want to use across multiple components without drilling props at every level in your component tree. A Redux store doesn't have a limit on the amount of data stored, so you can pretty much use it to store almost anything, including bulky JSON data.
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { store } from "./store";
import { Provider } from "react-redux";
import "./fontAwesome";
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
import { library } from "@fortawesome/fontawesome-svg-core";
import {
faGithubAlt,
faGoogle,
faFacebook,
faTwitter,
faJediOrder,
} from "@fortawesome/free-brands-svg-icons";
library.add(faGithubAlt, faGoogle, faFacebook, faTwitter, faJediOrder);
A store holds the whole state tree of your application.
The only way to change the state inside is to dispatch an action on it. The store will then pass the new state received from the reducer to the component.
import thunkMiddleware from "redux-thunk";
import { createStore, applyMiddleware } from "redux";
import reducer from "./reducer";
export const store = createStore(reducer, applyMiddleware(thunkMiddleware));
[
{
"id": 1,
"name": "Daniel",
"email": "[email protected]",
"post": "I love football",
"votes": 5
}
]
We import the following hooks from react-redux: useSelector
and useDispatch
.
useSelector
is a function that takes the current state as an argument and returns whatever data you want from it and it allows you to store the return values inside a variable within the scope of your functional components instead of passing them down as props.
useDispatch
is equivalent to mapDispatchToProps
. We will invoke useDispatch
and store it to a variable, dispatch. This hook returns a reference to the dispatch function from the Redux store.
You may use it to dispatch actions as needed. And we dispatch it by calling dispatch passing in the return value from the action creator.
import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import { requestUsers } from "./action";
import data from "./data.json";
import "./App.css";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
const App = () => {
const { usersData, isLoading } = useSelector((state) => state);
const dispatch = useDispatch();
useEffect(() => {
dispatch(requestUsers(data));
}, []);
return (
<>
{isLoading && <div className="loading">Data loading...</div>}
{usersData.map((user) => {
return (
<div key={user.id} className="container">
<div className="content">
<h1>{user.name}</h1>
<span>{user.email}</span>
<h3>{user.post}</h3>
Votes: {user.votes}
{user.votes >= 5 ? (
<div>
<FontAwesomeIcon icon={["fab", "jedi-order"]} size="3x" />
</div>
) : (
<p>Get up to five votes to have a badge</p>
)}
</div>
</div>
);
})}
</>
);
};
export default App;
Redux is a predictable state container for javascript.
So in a redux data flow, we have the javascript app which is the state of the app is maintained in the redux store, however, the app cannot directly update the state, it uses an action or it dispatches an action to maintain state. once the action has been dispatch the reducer handles the action and updates the current store.
Redux is very useful in a Single Page Application(SPA), because it produces a single source of truth. Redux has a store, the store is an object that contains your whole application state. The different pieces of states are stored in an object tree.
To read more about React-Redux you can go through this documentation.
Happy coding!