In the process of using React and Redux, there is always a problem: which states need to be stored in REdux, and which states need to be stored in local states within components. Besides, improper use of Redux may cause confusion in state management. React hooks provide a better alternative to setState in class. This article focuses on state management, using hooks elegantly to manage state.

  • How do I use Redux
  • React hooks manage local state
  • React hooks How do react hooks handle communication between components

Originally posted on my blog: github.com/fortheallli… Welcome to subscribe to


How to use Redux

It is important to understand why you are using ReDUx in the first place, because if you do not know why you are using ReDUx, you cannot use Redux properly during development. Let’s start with the essence of Redux:

Redux is a state management tool designed to solve the problem of communication between components.

Since this is a communication problem between components, it is obviously unreasonable and complex to put the state of all pages into Redux.

(1) Full use of Redux

The author also made this problem in the early stage. In the application, no matter what state, the page-level routing is split, all in Redux, and the change of any state of the page is realized through mapState and mapDispatch of React-Redux.

The process chain from state update to feedback to view in REDUx is too long. Start from dispatch with an action and then go to reducer and other logic, a complete link includes:

Create actions, redux middleware, reducer functions of corresponding types, mapState and mapDispatch, etc.

If you kept all the states in Redux, you would have to go through these steps for each state, which would be tedious and undoubtedly increase the amount of code

(2) Reduce the unreasonable mixing of local state and Redux state

The complexity of using redux fully is high, and we certainly consider putting some state in REDUx and some state in local state, but in this case, it is easy to have a problem if local state is state dependent on state in REdux.

For example, there are 10 students in the state in Redux

    //redux
    
    students = [{name:"Xiao Ming".score:70}, {name:"Little red".score:50}....]
Copy the code

In local State, we saved students with scores above 60


    // local state
    
    state = [{name:"Xiao Ming".score:70}]
Copy the code

If the students in redux change, we need to dynamically obtain the information of the students from redux, and then change the local state. Combined with the react – story, we need to use in container components componentWillReceivedProps or getDerivedStateFromProps this statement cycle, according to the props to change local local state.

ComponentWillReceivedProps don’t discuss here, for the sake of higher security, in the react with static getDerivedStateFromProps replaces the componentWillReceivedProps don’t discuss here, The getDerivedStateFromProps declaration cycle function is executed when the props and state change. Therefore, if we need to change the local state only when the props change, there will be complex judgment logic in this declaration cycle.

The more states in redux are related to states in local State, the more complex the declaration cycle function getDerivedStateFromProps becomes

If redux is associated with local state, it is better to use ids than objects or arrays. For example, we can group students and give them a group number. The group number changes every time the student information in Redux changes. GetDerivedStateFromProps only needs to check whether the group number has changed:


    class Container extends React.Component{
      state = {
         group_id:number
      }
      
      static getDerivedStateFromProps(props,state){
         if(props.group_id! ==state.group_id){ ... Update passing students}else{
            return null}}}Copy the code

Here recommend https://github.com/paularmstrong/normalizr, if the true story and a close correlation between the local state, can Fan Shihua data first, after Fan Shihua data is similar to an id for a complex structure, This simplifies the logic of getDerivedStateFromProps.

(3) Summary of this section

The nature of Redux is to solve the problem of communication between components. Therefore, unique state within components should not be placed in Redux. Moreover, if Redux is used in combination with class components, it can formalize data and simplify complex judgment logic.

React hooks Manage local state

Now that you’ve seen how redux should be used, how do you maintain local state? React16.8 officially added hooks. Manage local state with hooks, easy to use and extensible.

There are three common local states in hooks: useState, useRef, and useReducer

(1) useState

UseState is the most common local state in hooks, such as:

    const [hide, setHide] = React.useState(false);
    const [name, setName] = React.useState('BI');
Copy the code

UseState must be understood explicitly, in React hooks:

Each render has its own Props and State

A classic example is:

    function Counter() {
      const [count, setCount] = useState(0);
    
      function handleAlertClick() {
        setTimeout((a)= > {
          alert('You clicked on: ' + count);
        }, 3000);
      }
    
      return (
        <div>
          <p>You clicked {count} times</p>
          <button onClick={()= > setCount(count + 1)}>
            Click me
          </button>
          <button onClick={handleAlertClick}>
            Show alert
          </button>
        </div>
      );
    }
Copy the code

If I follow these steps:

  • Click to increase counter to 3
  • Click on “Show Alert”
  • Click to increase counter to 5 and complete before the timer callback triggers

Guess what the alert says?

The result is that a 3 pops up, and the Alert “captures” the state when I click the button, meaning that each render has its own props and state.

(2) useRef

In React hooks, we know that each render has its own props and state, so what if we need to get the latest render value every time like a class component? At this point we can use useRef

UseRef provides data that is Mutable

Let’s change the above example to alert 5:

    function Counter() {
        const [count, setCount] = useState(0)
        const late = useRef(0)
        function handleAlertClick() {
            setTimeout((a)= > {
                alert('You clicked on: ' + late.current)
            }, 3000)
        }
        useEffect((a)= > {
            late.current = count
        })
        return (
            <div>
                <p>You clicked {count} times</p>
                <button onClick={()= > setCount(count + 1)}>Click me</button>
                <button onClick={handleAlertClick}>Show alert</button>
            </div>)}Copy the code

So instead of alert3, pop 5

(3) useReducer

React Hooks also provide useReducer to manage local state.

When you want to update a state that depends on the value of another state, you may need to replace them with useReducer.

Here’s another example:

    function Counter() {
      const [state, dispatch] = useReducer(reducer, initialState);
      const { count, step } = state;
    
      useEffect((a)= > {
        const id = setInterval((a)= > {
          dispatch({ type: 'tick' });
        }, 1000);
        return (a)= > clearInterval(id);
      }, [dispatch]);
    
      return( <> <h1>{count}</h1> <input value={step} onChange={e => { dispatch({ type: 'step', step: Number(e.target.value) }); }} / > < / a >); } const initialState = { count: 0, step: 1, }; function reducer(state, action) { const { count, step } = state; if (action.type === 'tick') { return { count: count + step, step }; } else if (action.type === 'step') { return { count, step: action.step }; } else { throw new Error(); }}Copy the code

To explain the above results, see the useEffect section:

    useEffect((a)= > {
        const id = setInterval((a)= > {
          dispatch({ type: 'tick' });
        }, 1000);
        return (a)= > clearInterval(id);
      }, [dispatch]);
Copy the code

Count in state depends on step, but with the use of useReducer, we do not need to use step in the dependency change array of useEffect, and use Dispatch instead, which has the advantage of reducing unnecessary rendering behavior.

In addition, it is not recommended to use useReducer for local states. As a result, the internal states of functions are too complex and difficult to read. UseReducer is recommended to be used in combination with useContext for communication between multiple components.

React hooks How do you use react hooks to communicate between components

Local state management in React Hooks is much more brief than it is for class components, so how do we resolve communication between components if we use React hooks in our component?

###(1) UseContext

The most basic idea is probably to use useContext to solve the problem of communication between components.

Such as:

function useCounter() {
  let [count, setCount] = useState(0)
  let decrement = (a)= > setCount(count - 1)
  let increment = (a)= > setCount(count + 1)
  return { count, decrement, increment }
}

let Counter = createContext(null)

function CounterDisplay() {
  let counter = useContext(Counter)
  return (
    <div>
      <button onClick={counter.decrement}>-</button>
      <p>You clicked {counter.count} times</p>
      <button onClick={counter.increment}>+</button>
    </div>)}function App() {
  let counter = useCounter()
  return (
    <Counter.Provider value={counter}>
      <CounterDisplay />
      <CounterDisplay />
    </Counter.Provider>)}Copy the code

In this example, by createContext and useContext, you can use the context in the App’s subcomponent CounterDisplay to achieve some sense of component communication.

In addition, the industry has several simpler packages based on useContext for its integrity:

Github.com/jamiebuilds… Github.com/diegohaz/co…

But none of these essentially solve a problem:

If you have too many contexts, how do you maintain them

This means that in a scenario where a large number of components communicate, the code using context to communicate components is not readable. Context is not a new thing, although the use of useContext reduces the complexity of using context.

###(2) Redux implements communication between components using hooks

Redux can also be used to communicate between hooks components. In other words:

Redux also makes sense in React hooks

There is a problem with hooks because there is no high-order component like connect in React-redux, Redux-react-hook or 7.1 hooks version of react-redux.

  • redux-react-hook

Redux-react -hook provides StoreContext, useDispatch, and useMappedState to operate stores in Redux. For example, mapState and mapDispatch can be defined as follows:

import {StoreContext} from 'redux-react-hook';

ReactDOM.render(
  <StoreContext.Provider value={store}>
    <App />
  </StoreContext.Provider>,
  document.getElementById('root'),
);

import {useDispatch, useMappedState} from 'redux-react-hook';

export function DeleteButton({index}) {
  // Declare your memoized mapState function
  const mapState = useCallback(
    state => ({
      canDelete: state.todos[index].canDelete,
      name: state.todos[index].name,
    }),
    [index],
  );

  // Get data from and subscribe to the store
  const {canDelete, name} = useMappedState(mapState);

  // Create actions
  const dispatch = useDispatch();
  const deleteTodo = useCallback(
    () =>
      dispatch({
        type: 'delete todo',
        index,
      }),
    [index],
  );

  return (
    <button disabled={! canDelete} onClick={deleteTodo}>
      Delete {name}
    </button>
  );
}
Copy the code
  • React-redux 7.1 in hooks

React-redux hooks provide three main methods: useSelector(), useDispatch(), useStore() Corresponds to mapState, mapDispatch, and store instances directly in Redux.

A brief introduction to useSelector. UseSelector not only can get state from store, but also supports the function of deep comparison. If the corresponding state does not change before and after, it will not recalculate.

For example, the most basic usage:

import React from 'react'
import { useSelector } from 'react-redux'

export const TodoListItem = props= > {
  const todo = useSelector(state= > state.todos[props.id])
  return <div>{todo.text}</div>
}
Copy the code

Implement the cache function usage:

import React from 'react'
import { useSelector } from 'react-redux'
import { createSelector } from 'reselect'

const selectNumOfDoneTodos = createSelector(
  state= > state.todos,
  todos => todos.filter(todo= > todo.isDone).length
)

export const DoneTodosCounter = (a)= > {
  const NumOfDoneTodos = useSelector(selectNumOfDoneTodos)
  return <div>{NumOfDoneTodos}</div>
}

export const App = (a)= > {
  return (
    <>
      <span>Number of done todos:</span>
      <DoneTodosCounter />
    </>)}Copy the code

In the above cache usage, as long as todos.filter(todo => todo.isdone).length does not change, recalculation is not done.

Four,

The complete state management in React is divided into global state and local state. The React hooks simplify local state management, making it extremely convenient to manage local state and control local rendering. However, the React hooks are essentially a view component layer, and do not perfectly solve the communication problem between components. State managers like Redux and React hooks are not inherently contradictory.

In my practice, redux is used for component communication and React hooks are used for local state management, making code simple to read and eliminating unnecessary Redux boilerplate code.