Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

Today I’m going to use a simple blog application to show how React uses three different state management schemes.

  1. Pure React hooks
  2. Redux
  3. Context

Let’s look at each

Pure React Hooks

When our application is not large enough, the reactive data processing method of React Hools is sufficient for our daily application development.

example

import Posts from "./Components/Posts";
import PostForm from "./Components/PostForm";
import { useState } from "react";

const App = () = > {
  const [PostData, setPostData] = useState([]);

  const addPost = (formData) = > {
    setPostData([formData, ...PostData]);
  };

  return (
    <div className="app-container">
    	<h1>The React hooks way</h1>
      <PostForm addPost={addPost} />
      <Posts setPostData={setPostData} PostData={PostData} />
    </div>
  );
};

export default App;
Copy the code

In the code above, both the PostForm component and the Posts component need to pass in the same props. If there are many sibling components in the application, then we need to pass in props for each component.

If you have a nested multi-layer component structure, then you need to pass layers of props down to the deepest child component.

So that’s where we needed a place to centrally manage our data, and that’s where Redux came in

State management Redux

Redux is the best known and earliest and most widely used state management solution.

But learning Redux can also be a headache, with a lot of concepts to know and a lot of code to configure Redux in a project.

Redux is based on the great Flux architecture, which mainly provides Actions and reducers and a global store to let us manage state.

The Flux architecture can be understood as event-driven states, such as when a user clicks a button, submits a form, loads a page for the first time, and so on. When these actions occur, the Action handler function will be triggered to update the state, and the user interface will also change as a result of the state change. The components that need to update the state will be subscribed in the global Store, and once the Reducers make any changes to the state, they will immediately receive the updates. Simple to understand is the publish and subscribe design pattern.

Redux state management in React requires two dependent libraries: Redux and React-Redux.

example


import { Provider } from "react-redux";
import store from "./store";

import Posts from "./Components/Posts";
import PostForm from "./Components/PostForm";

const App = () = > (
  <Provider store={store}>
    <div className="app-container">
      <h1>Use Redux to manage state</h1>
      <PostForm />
      <Posts />
    </div>
  </Provider>
);

export default App;
Copy the code

At this point, we can see that the PostForm component and the Posts component no longer need to pass props to use and modify state in the global store within the component.

But, and yet, we need to write a lot more code inside the component to achieve this, focusing on the last two lines of the component.

PostForm component code

import { useState } from "react";
import { connect } from "react-redux";
import { object } from "prop-types";
import { createNewPost } from ".. /actions/postActions";

const initialFormState = { title: "".body: "" };

const PostForm = ({ createNewPost }) = > {
  const [formData, setFormData] = useState(initialFormState);

  const handleChange = (ev) = >{ setFormData({ ... formData, [ev.target.name]: ev.target.value, }); };const handlePostIt = (ev) = > {
    ev.preventDefault();
    createNewPost(formData);
    setFormData(initialFormState);
  };

  return (
    <div className="postform-container">
      <label htmlFor="title">Title</label>
      <input
        type="text"
        name="title"
        onChange={handleChange}
        value={formData.title}
      />
      <br />
      <label htmlFor="body">Post</label>
      <textarea name="body" onChange={handleChange} value={formData.body} />
      <br />
      <button type="submit" onClick={handlePostIt}>release</button>
    </div>
  );
};

const mapStateToProps = (state) = > ({});
export default connect(mapStateToProps, { createNewPost })(PostForm);
Copy the code

Posts component code

import { useEffect } from "react";
import { func, array, object } from "prop-types";
import { connect } from "react-redux";
import { fetchPosts } from ".. /actions/postActions";

const Posts = ({ posts, fetchPosts }) = > {
  useEffect(() = > {
    fetch("/posts")
      .then((res) = > res.json())
      .then((posts) = >{ fetchPosts(posts); }); } []);return (
    <div className="posts-container">
      <div>
        <h1>Post a list of</h1>
      </div>
      {posts.map((post, index) => (
        <div key={index}>
          <div className="post-title">{post.title}</div>
          <div className="post-body">{post.body}</div>
        </div>
      ))}
    </div>
  );
};

Posts.propTypes = {
  posts: array.isRequired,
};

const mapStateToProps = (state) = > ({
  posts: state.posts.items,
});
export default connect(mapStateToProps, { fetchPosts })(Posts);
Copy the code

You need to write this code after each component.

At this point you might say, this is not much different from the React hooks approach, but where are the pain points with Redux?

Then look at

Reducers/index. Js file

import { combineReducers } from "redux";
import postReducer from "./postReducer";

export default combineReducers({
  posts: postReducer,
});
Copy the code

The actions file

import { FETCH_POSTS, NEW_POST } from "./types";

export const fetchPosts = (postsData) = > {
  return {
    type: FETCH_POSTS,
    payload: postsData,
  };
};

export const createNewPost = (postData) = > {
  return {
    type: NEW_POST,
    payload: postData,
  };
};
Copy the code

Post reducers file

import { FETCH_POSTS, NEW_POST } from ".. /actions/types";

const initialState = {
  items: [].item: { title: "".body: ""}};const postReducer = (state = initialState, action) = > {
  if (action.type === FETCH_POSTS) {
    return {
      ...state,
      items: action.payload,
    };
  } else if (action.type === NEW_POST) {
    return {
      ...state,
      items: [action.payload, ...state.items],
    };
  } else return state;
};

export default postReducer;
Copy the code

Store status file

import { createStore } from "redux";
import rootReducer from "./reducers";

const initialState = {
  posts: {
    items: [].item: { title: "Title".body: "Content",}}};const store = createStore(rootReducer, initialState);

export default store;
Copy the code

Types file

export const FETCH_POSTS = "FETCH_POSTS";
export const NEW_POST = "NEW_POST";
Copy the code

This is the complete configuration required to use Redux.

The Context way

If there is now a way to have Redux’s state management architecture without a lot of configuration code or external dependencies.

Yes, that’s why React released Context as a built-in feature, allowing us to create global states. You can configure it in just a few lines.

Using the useReducer hook, you can emulate the Pattern of Redux. Using the Context, you can access the global state anywhere in the program, just by using the provider wrap at the root node.

Look at the code below

App.js

import Posts from "./Components/Posts";
import PostForm from "./Components/PostForm";
import { useEffect, createContext, useReducer } from "react";

const appReducer = (state, action) = > {
  switch (action.type) {
    case "FETCH_POSTS":
      return [...state, ...action.payload];
    case "NEW_POST":
      return [action.payload, ...state];
    default:
      returnstate; }};export const AppContext = createContext();

const App = () = > {
  const [state, dispatch] = useReducer(appReducer, []);

  return (
    <AppContext.Provider value={[state, dispatch]} >
      <div className="app-container">
        <h1>Context State Management</h1>
        <PostForm />
        <Posts />
      </div>
    </AppContext.Provider>
  );
};

export default App;
Copy the code

Posts.js

import { useEffect, useContext } from "react";
import { AppContext } from ".. /App";

const Posts = () = > {
  const [state, dispatch] = useContext(AppContext);

  useEffect(() = > {
    fetch("/posts")
      .then((res) = > res.json())
      .then((posts) = > {
        dispatch({ type: "FETCH_POSTS".payload: posts }); }); } []);return (
    <div className="posts-container">
      <div>
        <h1>Post a list of</h1>
      </div>
      {state.map((post, index) => (
        <div key={index}>
          <div className="post-title">{post.title}</div>
          <div className="post-body">{post.body}</div>
        </div>
      ))}
    </div>
  );
};

export default Posts;
Copy the code

PostForm.js

import { useState, useContext } from "react";
import { AppContext } from ".. /App";

const PostForm = () = > {
  const [state, dispatch] = useContext(AppContext);

  const [formData, setFormData] = useState({
    title: "".body: ""});const handleChange = (ev) = >{ setFormData({ ... formData, [ev.target.name]: ev.target.value, }); };const handlePostIt = (ev) = > {
    ev.preventDefault();
    dispatch({ type: "NEW_POST".payload: formData });
    setFormData({ title: "".body: "" });
  };

  return (
    <div className="postform-container">
      <label htmlFor="title">The title</label>
      <input
        type="text"
        name="title"
        onChange={handleChange}
        value={formData.title}
      />
      <br />
      <label htmlFor="body">content</label>
      <textarea name="body" onChange={handleChange} value={formData.body} />
      <br />
      <button type="submit" onClick={handlePostIt}>release</button>
    </div>
  );
};

export default PostForm;
Copy the code

The appReducer function in app.js is the equivalent of replacing the annoying code in Redux.

Use React Hooks and Context to implement global state management with less code, and these functions are already built into React, no longer requiring third-party dependencies.

However, Redux is not useless, and state management is not the only feature Redux provides, so you need the rest of Redux’s functionality, so keep using Redux.

In fact, these three methods are now common for React development. Using only hooks is suitable for small, simple applications, so only select Redux and Context when you need global state management.

The last

To quote Dan Abramov, creator of Redux

“If you don’t know if you need Redux, you don’t need it.”

“You only need Redux when React isn’t really a solution.”

Thank you for reading this article. If you found it helpful, please like it, comment it, and follow it

Also welcome to follow my official account (click follow), every day to update a useful content.