如何从React-Redux类转换为React Hooks的简单方法

孟意致
2023-12-01

Hello everyone! With the recent release of create-react-app v3 and React hooks, I decided to write a tutorial on how to refactor a class component to a functional hooks component.

大家好! 随着最近发布的create-react-app v3和React钩子,我决定写一篇关于如何将类组件重构为功能性钩子组件的教程。

In this tutorial, I will share how I did it. I refer to this as the “easy way” since it doesn't require you to change your Redux code at all. The reducers and actions can be virtually left as is.

在本教程中,我将分享我的操作方式。 我将其称为“简便方法”,因为它根本不需要您更改Redux代码。 减速器和动作实际上可以保留。

If you want a more basic intro to React Hooks integration check out my previous tutorial.

如果您想对React Hooks集成进行更基本的介绍,请查看我以前的教程

You can find the project code here.

您可以在此处找到项目代码。

You can also follow me on twitter for more tutorials in the future: here

您也可以在Twitter上关注我,以获取将来的更多教程: 此处

This project uses both Redux and React hooks which will allow you to see the code and differences side by side. Open up the hooks_container1.js file and container1.js files in your text editor to see the differences. I tried my best to match the React class and React hook line for line making it easier to see the differences. However, it didn’t work out perfectly since there are some major differences between React Hooks and React classes. I tried to keep the functionality of both components the same so it will be easier for you to pick out the differences in syntax.

该项目同时使用Redux和React钩子,使您可以并排查看代码和差异。 在文本编辑器中打开hooks_container1.js文件和container1.js文件,以查看差异。 我尽力匹配React类和React钩子行,以方便查看差异。 但是,由于React Hooks和React类之间存在一些主要差异,因此效果并不理想。 我试图使两个组件的功能保持相同,以便您更容易找出语法上的差异。

目录 (Table of Contents)

  1. TLDR version

    TLDR版本
  2. useReducer and Context

    useReducer和上下文
  3. When to use local or global state and useState and useReducer

    何时使用本地或全局状态以及useState和useReducer
  4. How Context Works

    上下文如何工作
  5. Directory Structure

    目录结构
  6. The Context Object

    上下文对象
  7. Reducers and Actions

    减速器和动作
  8. Reading and Updating state in React Redux vs. React Hooks

    React Redux与React Hooks中的读取和更新状态
  9. Merging the old state in React Hooks

    在React Hooks中合并旧状态
  10. Reading and Updating state with useReducer and Redux Reducers

    使用useReducer和Redux Reducers读取和更新状态
  11. Reading state and Dispatching Actions

    读取状态和调度动作
  12. Context with useState

    使用状态的上下文
  13. Context with useReducer

    使用useReducer的上下文

简易方法— TL; DR (The Easy Way — TL;DR)

Step 1: For your reducers, export both the initialState and the reducer. Do not export default the reducer.

步骤1:对于您的reducer,请同时导出initialState和reducer。 不要export default的减速器。

Step 2: Actions can be left as is from React-Redux

第2步:可以将动作保留在React-Redux中

Step 3: Import all your reducers and their initialState to the root App.js file. Import actions as normal.

步骤3:将所有的reducer及其initialState导入到根App.js文件。 正常导入操作。

Step 4: Pass in each reducer and its initialState to a separate useReducer() hook in the App.js file.

步骤4:将每个reducer及其initialState传递到App.js文件中的单独的useReducer()挂钩。

Step 5: Import the React.createContext() function to App.js after initializing it in its own file. Wrap all child components with <Context.Provider />

步骤5:在自己的文件中对其进行初始化后,将React.createContext()函数导入React.createContext()<Context.Provider />包装所有子组件

Step 6: Next simply cut and paste the properties defined in your React-Redux mapStateToProps() and mapDispatchToProps() functions to the value prop of <Context.Provider />

步骤6:接下来,只需将在React-Redux mapStateToProps()mapDispatchToProps()函数中定义的属性剪切并粘贴到<Context.Provider />value prop

Step 7: Change the dispatch keyword in your properties from the mapDispatchToProps() function to the name of the dispatch actions function (2nd element in the array destructuring) in the useReducer() hook. Since each reducer will have its own useReducer hook, you will have to match the appropriate action dispatches with the right reducer.

步骤7:将属性中的dispatch关键字从mapDispatchToProps()函数更改为useReducer()挂钩中的dispatch action函数的名称(数组解构中的第二个元素useReducer() 。 由于每个reducer都有自己的useReducer钩子,因此您必须将适当的动作分派与正确的reducer进行匹配。

Step 8: Do the same thing for the mapStateToProps() function. Change the name of the property to match the useReducer hook. The state value for the useReducer() hook (1st element in the array destructuring) contains the entire initial state from the reducer. You will need to access each property of state with dot notation, and then pass it into a property in the “value” prop.

步骤8:mapStateToProps()函数执行相同的mapStateToProps() 。 更改属性的名称以匹配useReducer钩子。 useReducer()挂钩(数组解构中的第一个元素useReducer()的状态值包含来自reducer的整个初始状态。 您将需要使用点表示法访问状态的每个属性,然后将其传递给“值”属性中的属性。

Step 9: Finally to actually use the global Context state in a child component, you first import the original Context object to the child component. Then pass in the imported Context object to the useContext() hook. Save the result of the useContext hooks in a variable. Now you have access to all the properties that we defined in the value prop of the <Context.Provider /> in the root App.js file.

步骤9:最后,要在子组件中实际使用全局Context状态,首先需要将原始Context对象导入到子组件中。 然后将导入的Context对象传递给useContext()挂钩。 将useContext挂钩的结果保存在变量中。 现在,您可以访问我们在根App.js文件的<Context.Provider />value属性中定义的所有属性。

Accessing state values in a child component with context: context.stateprop1

使用上下文访问子组件中的状态值: context.stateprop1

Dispatching actions in a child component with context: () => context.action1()

使用context () => context.action1()在子组件中分派动作

Here is an example of a React Redux class container and a React Hooks functional component with similar functionality side by side.

这是一个具有相似功能的React Redux类容器和React Hooks功能组件的示例。

class Container1 extends Component {
    constructor(props) {
      super(props)

      this.state = {
        local_state_prop1: true,
        local_state_prop2: 0,
        cDM_value: ''
      }
    }

 ...
 
    inc_local = () => {
      this.setState({local_state_prop2: this.state.local_state_prop2 + 1})
    }

    dec_local = () => {
      this.setState({local_state_prop2: this.state.local_state_prop2 - 1})
    }
 
  ...
    <button onClick={() => this.inc_local()}> INC Local State  </button>
    <button onClick={() => this.dec_local()}> DEC Local State  </button>
    <br />
    <br />
      {this.state.local_state_prop2}
    <br />
 ...
const HooksContainer1 = () => {
    const [value, setValue] = useState({local_state_prop1: true,
                                        local_state_prop2: 0
                                       })
  
...    
    const incrementValue_uS = () => {
      setValue({...value, local_state_prop2: value.local_state_prop2 + 1} )
    }

    const decrementValue_uS = () => {
      setValue({...value, local_state_prop2: value.local_state_prop2 - 1} )
    }
    
...

      <button onClick={() => incrementValue_uS()}> Add Local Value uS </button>
      <button onClick={() => decrementValue_uS()}> Dec Local Value uS</button>
      <br />
      <p>Local useState Value: {value.local_state_prop2}</p>
      <br />
 ...

Before getting started, I’d like to clarify a few things that confused me when I first started working with React Hooks.

在开始之前,我想澄清一些让我第一次开始使用React Hooks时困惑的事情。

useReducer和上下文 (useReducer and Context)

I was confused a little at first by useReducer. I thought by simply using useReducer, I would have automatically mimicked Redux functionality and have a global state. That isn’t the case. It is Context that makes our state global. Context can be used with either useReducer and useState.

起初我被useReducer弄糊涂了。 我认为只要使用useReducer,我就可以自动模仿Redux功能并具有全局状态。 事实并非如此。 正是上下文使我们的国家具有全球性。 上下文可以与useReducer和useState一起使用。

global state: meaning state persists from one component to another. If you changed state in one component and went to another component, the state would be saved if it is global. If the state is local and you went to another component the state would not be saved.

全局状态 :表示状态从一个组件持续到另一个组件。 如果您更改了一个组件中的状态并转到了另一个组件,则状态为全局时将被保存。 如果状态为本地,而您转到了另一个组件,则该状态将不会保存。

何时使用本地或全局状态以及useState和useReducer (When to use local or global state and useState and useReducer)

For teaching purposes, I will show you all four possible combinations of local and global state with useState and useReducer. In a real app, I would use the useReducer hook for complex global states, such as authentication and storing data from a server. I would use the useState hook for simpler local state, such as opening up and closing a modal.

出于教学目的,我将向您展示useState和useReducer的本地和全局状态的所有四种可能组合。 在真实的应用程序中,我将使用useReducer钩子来处理复杂的全局状态,例如身份验证和从服务器存储数据。 我会使用useState钩子来获取更简单的本地状态,例如打开和关闭模式。

上下文如何工作 (How Context Works)

Context predates React hooks and is a way to pass down props to deeply nested child components. Without context, props would have to be passed down to every intermediary component to get to the intended child component.

上下文早于React钩子,是将props传递到深度嵌套的子组件的一种方式。 在没有上下文的情况下,必须将道具向下传递到每个中间组件,才能到达预期的子组件。

Context solved this by allowing you to pass in a prop to the parent component. Then it would be available to all the child components automatically. You did not have to pass it down through middle man components. And this is essentially how we have a global state. By using the Context in the root component, our state is available to all child components. Since App.js is the root component, and every other component is the child component, the state we defined in App.js is available to all components.

Context通过允许您将prop传递给父组件来解决了这个问题。 然后,它将自动对所有子组件可用。 您不必将其传递给中间人组件。 这实质上就是我们拥有全球状态的方式。 通过在根组件中使用上下文,我们的状态可用于所有子组件。 由于App.js是根组件,其他所有组件都是子组件,因此我们在App.js中定义的状态适用于所有组件。

It’s important to keep in mind that all the state is contained, initialized and updated in the App.js file. You can call a function to change the state from a child component but it is ultimately updated in the App.js file.

重要的是要记住,所有状态都包含在App.js文件中,进行了初始化和更新。 您可以调用一个函数来更改子组件的状态,但最终会在App.js文件中对其进行更新。

目录结构和简介 (Directory Structure and Intro)

Instead of focusing on how to build this app step by step, I will instead focus more on the differences between the React-Redux class and the React hooks.

我将不再专注于如何逐步构建此应用程序,而将更多地关注于React-Redux类与React钩子之间的差异。

Here are a couple of acronyms I use and their meaning

这是我使用的几个缩写词及其含义

uS = useState signifies when something is using the useState hook

uS = useState表示什么东西正在使用useState钩子

uR = useReducer signifies when something is using the useReducer hook

UR = useReducer表示某物时使用useReducer钩

Here is the directory structure. It is a very basic app that has:

这是目录结构。 这是一个非常基本的应用程序,具有:

  • 1 React-Redux class

    1个React-Redux类
  • 1 React functional component that uses the useState, useReducer, and useContext hooks

    1个使用useState,useReducer和useContext挂钩的React功能组件
  • Actions and action types

    动作和动作类型
  • Reducers to use with React hooks

    减速器与React挂钩一起使用
  • Reducers to use with React-Redux

    减速器与React-Redux一起使用
  • a Context file

    上下文文件
  • The root App.js file

    根App.js文件

上下文对象 (The Context Object)

I like to have context in its own file since you have to import it to every child component that you use with the useContext() hook. We don’t need to do anything else to setup the Context object, we just need this one function.

我喜欢在自己的文件中包含上下文,因为您必须将其导入到与useContext()挂钩一起使用的每个子组件中。 我们不需要做任何其他事情来设置Context对象,我们只需要一个函数。

import React from 'react';

const Context = React.createContext()

export default Context;

Also, notice we are not passing in any state to the Context object. You may see other tutorials that pass values to the createContext() function. This is pointless as we will override these values when we setup the <Context.Provider /> and pass in the state to the value prop.

另外,请注意,我们没有将任何状态传递给Context对象。 您可能会看到其他将值传递给createContext()函数的教程。 这毫无意义,因为在设置<Context.Provider />并将状态传递给value prop时,我们将覆盖这些值。

减速器和动作 (Reducers and Actions)

Now I will show a reducer for use with React Hooks and one for use with regular React Redux.

现在,我将展示与React Hooks一起使用的减速器,以及与常规React Redux一起使用的减速器。

Reducer for use with React Hooks:

用于React Hooks的Reducer:

import * as ACTION_TYPES from '../actions/action_types'

export const initialState = {
  hooks_stateprop1: false,
}

export const Reducer1 = (state = initialState, action) => {
    switch(action.type) {
      case ACTION_TYPES.SUCCESS:
        return {
          ...state,
          hooks_stateprop1: true,
        }
      case ACTION_TYPES.FAILURE:
        return {
          ...state,
          hooks_stateprop1: false,
        }
      default:
        throw new Error();
    }
}

Reducer for React Redux:

用于React Redux的Reducer:

import * as ACTION_TYPES from '../actions/action_types'

const initialState = {
  stateprop1: false
}

const Reducer1 = (state = initialState, action) => {
    switch(action.type) {
      case ACTION_TYPES.SUCCESS:
        return {
          ...state,
          stateprop1: true
        }
      case ACTION_TYPES.FAILURE:
        return {
          ...state,
          stateprop1: false
        }
      default:
        return state
    }
}

export default Reducer1;

Notice in the React Hooks reducer we are exporting both the intialState and reducer. We are not using export default at the bottom. In the React Redux reducer we export default the reducer.

注意,在React Hooks缩减器中,我们同时导出了intialState和缩减器。 我们不在底部使用export default 。 在React Redux减速器中,我们export default减速器。

Next, we have our actions and action types:

接下来,我们有我们的动作和动作类型:

export const SUCCESS = "SUCCESS"

export const FAILURE = "FAILURE"

...
import * as ACTION_TYPES from './action_types'

export const SUCCESS = {
  type: ACTION_TYPES.SUCCESS
}

export const FAILURE = {
  type: ACTION_TYPES.FAILURE
}

export const success = () => {
  return {
    type: ACTION_TYPES.SUCCESS
  }
}

export const failure = () => {
  return {
    type: ACTION_TYPES.FAILURE
  }
}
...

Actions and action creators require no changes from React Redux.

动作和动作创建者无需从React Redux进行任何更改。

React Redux与React Hooks中的读取和更新状态 (Reading and Updating state in React Redux vs React Hooks)

With the preliminary information out of the way, we can now look at the hooks_container1.js and container1.js and see the differences between React Hooks and React Redux in the code.

有了初步的信息,我们现在可以查看hooks_container1.jscontainer1.js ,并在代码中看到React Hooks和React Redux之间的区别。

Let’s start off and look at local state for each and see how you would implement a simple counter.

让我们开始查看每个状态的本地状态,看看如何实现一个简单的计数器。

React-Redux

React-Redux

class Container1 extends Component {
    constructor(props) {
      super(props)

      this.state = {
        local_state_prop1: true,
        local_state_prop2: 0,
        cDM_value: ''
      }
    }

 ...
 
    inc_local = () => {
      this.setState({local_state_prop2: this.state.local_state_prop2 + 1})
    }

    dec_local = () => {
      this.setState({local_state_prop2: this.state.local_state_prop2 - 1})
    }
 
  ...
    <button onClick={() => this.inc_local()}> INC Local State  </button>
    <button onClick={() => this.dec_local()}> DEC Local State  </button>
    <br />
    <br />
      {this.state.local_state_prop2}
    <br />
 ...

React Hooks

React钩

const HooksContainer1 = () => {
    const [value, setValue] = useState({local_state_prop1: true,
                                        local_state_prop2: 0
                                       })
  
...    
    const incrementValue_uS = () => {
      setValue({...value, local_state_prop2: value.local_state_prop2 + 1} )
    }

    const decrementValue_uS = () => {
      setValue({...value, local_state_prop2: value.local_state_prop2 - 1} )
    }
    
...

      <button onClick={() => incrementValue_uS()}> Add Local Value uS </button>
      <button onClick={() => decrementValue_uS()}> Dec Local Value uS</button>
      <br />
      <p>Local useState Value: {value.local_state_prop2}</p>
      <br />
 ...

The first thing to note is that we are going from using a class component in React Redux to a functional component in React Hooks. Hence why we don't have the “this” keyword anywhere in our React Hooks code. Since we are not in a class, we can reference the variable and function names directly.

首先要注意的是,我们将从使用React Redux中的类组件到使用React Hooks中的功能组件。 因此,为什么我们在React Hooks代码的任何地方都没有“ this”关键字。 由于我们不在类中,因此可以直接引用变量和函数名称。

In React Redux we initialize the state in the constructor and have a dedicated setState() function. Both “state” and “setState()” are reserved names.

在React Redux中,我们在构造函数中初始化状态,并具有专用的setState()函数。 “状态”和“ setState()”都是保留名称。

This is not so in React hooks. In React Hooks we create our own “state” keyword and setState() function ourselves with the useState() Hook. In the example above, you can think of value as the equivalent to this.state in a class component. And similar to this.state, we use dot notation to access each individual property of state, so the syntax we will be:

在React钩子中并非如此。 在React Hooks中,我们使用useState()Hook自己创建自己的“ state”关键字和setState()函数。 在上面的示例中,您可以将value视为类组件中this.state的等效this.state 。 类似于this.state ,我们使用点表示法访问state的每个单独属性,因此语法如下:

value.name_of_property

When I first started learning Hooks, I used to confuse the useState() hook as the equivalent to setState() function in React Redux. This isn’t the case. The React Redux setState() function is equivalent to the second element in the array destructuring. Which in the example above is setValue(). This setValue() function is how we update our state with hooks. useState() is then just a way we initialize the ability to read and update the state in a functional component. This previously was only available to class components.

当我第一次开始学习Hooks时,我曾经把useState()钩子等同于React Redux中的setState()函数。 事实并非如此。 React Redux setState()函数等效于数组解构中的第二个元素。 在上面的示例中是setValue() 。 这个setValue()函数是我们如何使用钩子更新状态的方法。 useState()只是我们初始化读取和更新功能组件中状态的能力的一种方式。 以前这仅适用于类组件。

在React Hooks中合并旧状态 (Merging the old state in React Hooks)

Another important thing to notice in the React Hooks example is that I am using …value before updating the state in the increment and decrement functions. This is the spread operator, that passes in the entire flattened previous state to the setState() function.

在React Hooks示例中要注意的另一件重要事情是,在更新递增和递减函数中的状态之前,我正在使用…value 。 这是散布运算符,它将整个展平的先前状态传递给setState()函数。

I did not need to pass in the previous state in the React Redux example. When we update a state property in React Redux the new state property is automatically merged with the old state properties

我不需要在React Redux示例中传递先前的状态。 当我们在React Redux中更新状态属性时,新的状态属性会自动与旧的状态属性合并

This does not happen in React Hooks. When you update the state in React Hooks, a new state is created. You see in the React Hooks example we have 2 state properties: local_state_prop1 and local_state_prop2. If we update the state with only local_state_prop2 and not pass in …value then a new state will be created that has only local_state_prop2. Meaning our local_state_prop1 will simply just be deleted.

这在React Hooks 中不会发生。 当您在React Hooks中更新状态时,将创建一个新状态。 您可以在React Hooks示例中看到,我们有2个状态属性: local_state_prop1local_state_prop2 。 如果我们仅使用local_state_prop2更新状态而未传递…value则将创建仅具有local_state_prop2的新状态。 意味着我们的local_state_prop1只会被删除。

So when converting state from React Redux to React Hooks, you will need to pass in the entire previous state with the spread operator when updating a single state property.

因此,在将状态从React Redux转换为React Hooks时,在更新单个状态属性时,您需要使用传播运算符传递整个先前的状态。

使用useReducers和Redux Reducers读取和更新状态 (Reading and Updating state with useReducers and Redux Reducers)

We can now compare reading and updating state with useReducer and Reducers.

现在,我们可以使用useReducer和Reducers比较读取和更新状态。

We are using the same reducer as in the above example. A reducer with SUCCESS and FAILURE action types that changes stateprop1 from true to false and vice versa.

我们正在使用与上述示例相同的减速器。 具有SUCCESSFAILURE操作类型的化stateprop1 ,将stateprop1从true更改为false,反之亦然。

useReducer Hook

useReducer挂钩

import * as Reducer1 from '../store/hooks_reducers/reducer1_hooks';

...
const HooksContainer1 = () => {
      const [stateLocal1, dispatchLocal1] = useReducer(Reducer1.Reducer1,
                                                     Reducer1.initialState)
    
...

    const action1 = () => {
      //    dispatchLocal1({type: "SUCCESS"})
      //  dispatchLocal1(ACTIONS.success())
          dispatchLocal1(ACTIONS.SUCCESS)

    }

    const action2 = () => {
      //   dispatchLocal1({type: "FAILURE"})
      //   dispatchLocal1(ACTIONS.failure())
           dispatchLocal1(ACTIONS.FAILURE)

    }
 ...
 
    <button onClick={() => action1()}>Dispatch Action 1  </button>
    <button onClick={() => action2()}>Dispatch Action 2 </button>
      <br />
      {stateLocal1.stateprop1
        ? <p> stateprop1 is true </p>
        : <p> stateprop1 is false </p>
      }
      <br />
...

React Redux

React Redux

...

function mapStateToProps(state) {
  return {
    stateprop1: state.reducer1.stateprop1,
  }
}

function mapDispatchToProps(dispatch) {
  return {
    // action_creator1: () => dispatch(ACTIONS.success()),
    // action_creator2: () => dispatch(ACTIONS.failure()),
    // action_type1: () => dispatch({type: "SUCCESS"}),
    // action_type2: () => dispatch({type: "FAILURE}),
    action1: () => dispatch(ACTIONS.SUCCESS),
    action2: () => dispatch(ACTIONS.FAILURE),
  }
}

...

  <button onClick={() => this.props.action1()}> Dispatch Action 1 </button>
  <button onClick={() => this.props.action2()}>Dispatch Action 2 </button>
  <br />
  {this.props.stateprop1
    ? <p> stateprop1 is true </p>
    : <p> stateprop1 is false </p>
  }
  <br />
...

As mentioned in the intro, even though we are using useReducer() in the functional component, we are still only updating the local component state. I will show you how to mimic Redux functionality with Context and have a global state in the next section. It is important to keep in mind we are still only updating the local state here in our Hooks container even though we are using actions and reducers.

如简介中所述,即使我们在功能组件中使用useReducer() ,我们仍然仅更新本地组件状态。 在下一节中,我将向您展示如何模仿Context的Redux功能并具有全局状态。 重要的是要记住,即使我们正在使用动作和化简器,我们仍仅在Hooks容器中更新此处的本地状态。

On the other hand, in our React Class component, we are updating the global state since we are using Redux.

另一方面,在我们的React Class组件中,由于我们正在使用Redux,因此我们正在更新全局状态。

So the first difference you will notice with useReducer is that we have to import our reducer and initial state and pass it into the useReducer hook, which is something we don't do with React Redux. In React Redux we just use the connect() function.

因此,您将发现useReducer的第一个区别是,我们必须导入我们的reducer和初始状态,并将其传递到useReducer钩子中,而React Redux则不这样做。 在React Redux中,我们只使用connect()函数。

读取状态和调度动作 (Reading state and Dispatching Actions)

Next, to dispatch actions in React Hooks, we use an arrow function then dispatch our actions in the body of the function. You can dispatch actions directly in the onClick() event but having the dispatch in a function will make your code more readable.

接下来,要在React Hooks中分派动作,我们使用箭头函数,然后在函数主体中分派动作。 您可以直接在onClick()事件中分派动作,但在函数中具有分派将使您的代码更具可读性。

In React Redux we set properties in the mapDispatchToProps() function, then each property is an arrow function that dispatches actions.

在React Redux中,我们在mapDispatchToProps()函数中设置属性,然后每个属性都是一个分派动作的箭头函数。

You will notice that we pass in actions and actions creators in the exact same way to the dispatch function in both React Hooks and React Redux. There is literally no difference which is why we didn't need to change our actions at all. I have included all the ways of dispatching actions as a comment.

您会注意到,我们以完全相同的方式将动作和动作创建者传递给React Hooks和React Redux中的调度函数。 实际上没有什么区别,这就是为什么我们根本不需要更改我们的动作。 我已经将所有分派动作的方式都包括在内。

The only difference between React Hooks and React Redux is that the “dispatch” function name is reserved in React Redux. In React Hooks we create our own “dispatch” function name through the useReducer hook.

React Hooks和React Redux之间的唯一区别是“ dispatch”函数名称在React Redux中保留。 在React Hooks中,我们通过useReducer钩子创建自己的“ dispatch”函数名称。

To call the dispatch function in React Redux we use the syntax this.props then the name of the property in mapDispatchToProps() function. In React Hooks we just call the dispatch function name directly.

要在React Redux中调用调度函数,我们使用语法this.props然后在mapDispatchToProps()函数中使用属性名称。 在React Hooks中,我们只是直接调用调度函数名称。

To read the state in React Redux we do this.props and then the name of the property in the mapStateToProps() function. The name of the property holds the value for a specific property in a specific reducer. In React Hooks we just do the name of the state value. This is the first element in the array destructuring in the useReducer hook call. Then the name of the property we defined in the intialState in the reducer.

要读取React Redux中的状态,请执行this.props ,然后执行mapStateToProps()函数中的属性名称。 该属性的名称保存特定化简器中特定属性的值。 在React Hooks中,我们只需要输入状态值的名称。 这是useReducer挂钩调用中数组销毁中的第一个元素。 然后是我们在化简器的intialState中定义的属性的名称。

使用状态的上下文 (Context with useState)

Now I will go over Context which is how we setup a global state. It is important to note that Context is not part of React Hooks. useContext() is a React Hook, but Context itself is not part of React Hooks. Context is simply a way to pass down props from a parent component to a deeply nested child component. See the “How Context Works” section at the beginning of this tutorial for a full explanation.

现在,我将讨论Context,这是我们设置全局状态的方式。 重要的是要注意,上下文不是React Hooks的一部分。 useContext()是一个React Hook,但是Context本身不是React Hooks的一部分。 上下文只是将道具从父组件传递到深度嵌套的子组件的一种简单方法。 有关完整说明,请参见本教程开始的“上下文如何工作”部分。

Also, I will not be making comparisons between React Redux and Context because Context does not have an opposite in React Redux. I will show you how to implement a global state with Context using both the useReducer() and useState() hook.

另外,我不会在React Redux和Context之间进行比较,因为Context在React Redux中没有相反的含义。 我将向您展示如何使用useReducer()useState()钩子通过Context实现全局状态。

We will first start off with the using the useState() hook to setup a global state.

我们将首先使用useState()钩子设置全局状态。

We will begin setting up our global state in the root App.js file. We will first import the Context object we setup in the context.js file. We will also need to import our Hooks functional component.

我们将开始在根App.js文件中设置全局状态。 我们将首先导入我们在context.js文件中设置的Context对象。 我们还需要导入我们的Hooks功能组件。

import Context from '../utils/context';
import HooksContainer1 from './hooks/hooks_container1';
...

const App = () => {
    const [valueGlobal_uS, setValueGlobal_uS] = useState(0)
    
    const incrementValueGlobal_uS = () => {
      setValueGlobal_uS(valueGlobal_uS + 1 )
    }

    const decrementValueGlobal_uS = () => {
      setValueGlobal_uS(valueGlobal_uS - 1 )
    }
    
...    
    
    <div>
     <Context.Provider
          value={{
            //global state with useState
            valueGlobalState_uS: valueGlobal_uS,
            addGlobalValue_uS: () => incrementValueGlobal_uS(),
            decGlobalValue_uS: () => decrementValueGlobal_uS(),
          }}>
        <HooksContainer1 />
      </Context.Provider>
     </div>
...

We can just set up a simple counter for now. Our useState() hook is setup as usual. In our JSX we are wrapping our <HooksContainer1 /> with the <Context.Provider /> element. This is what allows us to pass state from App.js to child components. We also have 3 properties supplied to our value prop. 1 to hold the state value and 2 properties to change the state. Notice that we don't use the useContext() hook in App.js. The useContext() hook will actually be used in child components to read and update the state.

我们现在可以只设置一个简单的计数器。 我们的useState()钩子照常设置。 在我们的JSX中,我们用<Context.Provider />元素包装<HooksContainer1 /> 。 这就是允许我们将状态从App.js传递到子组件的原因。 我们还为我们的valueStruts提供了3个属性。 1用于保存状态值,而2用于更改状态的属性。 注意,我们不在useContext()使用useContext()挂钩。 useContext()挂钩实际上将在子组件中用于读取和更新状态。

You can essentially think of the value prop as both the mapStateToProps() and mapDispatchToProps() functions combined into one because of the value prop holds properties that allow you to read and update the state that can be called and accessed by the child component which is exactly what the mapStateToProps() and mapDispatchToProps() functions do.

实际上,您可以将value prop视为mapStateToProps()mapDispatchToProps()函数合并为一个函数,因为value prop拥有允许您读取和更新可mapDispatchToProps()组件调用和访问的状态的属性。正是mapStateToProps()mapDispatchToProps()函数的作用。

Now let's look at how we would use this Context object in a child component.

现在让我们看看如何在子组件中使用此Context对象。

import Context from '../utils/context';

...
const HooksContainer1 = () => {
  
...
    const context = useContext(Context)


    <p>Global useState Value: {context.valueGlobalState_uS}</p>

    <button onClick={() => context.addGlobalValue_uS()}> Add Global Value uS </button>
    <button onClick={() => context.decGlobalValue_uS()}> Dec Global Value uS </button>

...

We first have to import our Context object at the top. This is the original Context object that we created with the createContext() function, not the <Context.Provider /> we just setup. Then we simply pass this Context object to the useContext() hook and save it in a variable. This context variable now has all the properties we just defined in the value prop of the <Context.Provider />.

我们首先必须在顶部导入Context对象。 这是我们使用createContext()函数创建的原始Context对象,而不是我们刚刚设置的<Context.Provider /> 。 然后,我们只需将此Context对象传递给useContext()钩子,并将其保存在变量中。 现在,此上下文变量具有我们刚刚在<Context.Provider />value属性中定义的所有属性。

To access the properties of the value prop we can just use dot notation. For example, to access the state value here in our child component, we use the syntax context.valueGlobalstate_uS.

要访问value道具的属性,我们可以仅使用点表示法。 例如,要在子组件中访问状态值,我们使用语法context.valueGlobalstate_uS

Note that valueGlobalState is the name of the property we defined in the App.js file in the value prop. valueGlobalState is the property that holds the value of the state which in App.js we defined as valueGlobal_uS. Similarly, to change the state we call the property name and not the name of the function we set in App.js.

请注意, valueGlobalState是我们在值prop中的App.js文件中定义的属性的名称。 valueGlobalState是保存状态值的属性,在App.js中我们将其定义为valueGlobal_uS 。 同样,要更改状态,我们调用属性名称,而不是在App.js中设置的函数的名称。

I have intentionally kept the property and function names different so its easier to see how Context works in the child component.

我故意使属性和函数名称保持不同,以便更轻松地了解Context在子组件中的工作方式。

This is it for using Context with useState. I will now demonstrate with useReducer.

这是用于将Context与useState一起使用的。 我现在将用useReducer进行演示。

使用useReducer的上下文 (Context with useReducer)

Using Context with useReducer is essentially how we achieve Redux functionality.

在useReducer中使用Context本质上是我们实现Redux功能的方式。

In order to avoid confusion, I will setup a new reducer and actions for this.

为了避免混淆,我将为此设置一个新的reducer和操作。

export const CONTEXT_INC = "CONTEXT_INC"

export const CONTEXT_DEC = "CONTEXT_DEC"
export const CONTEXT_INC = {
  type: ACTION_TYPES.CONTEXT_INC
}

export const CONTEXT_DEC = {
  type: ACTION_TYPES.CONTEXT_DEC
}
import * as ACTION_TYPES from '../actions/action_types'

export const initialState = {
  context_prop1: 0,
}

export const ContextReducer = (state = initialState, action) => {
    switch(action.type) {
      case ACTION_TYPES.CONTEXT_INC:
        return {
          ...state,
          context_prop1: state.context_prop1 + 1
        }
      case ACTION_TYPES.CONTEXT_DEC:
        return {
          ...state,
          context_prop1: state.context_prop1 - 1
        }
      default:
        throw new Error();
    }
}

So we have a simple reducer that functions as a counter. Now we can set up the useReducer hook in our App.js file and we will set this up in the exact same way that we set up useReducer in our Hooks container. We import the ContextReducer and its initial state and pass it into the useReducer Hook in App.js. Because we are now using Context we will not import our Context Reducer to the child components. The state will be changed here in our App.js file and will simply be passed down as props.

因此,我们有一个简单的减速器作为计数器。 现在,我们可以在App.js文件中设置useReducer钩子,并将以与在Hooks容器中设置useReducer完全相同的方式进行设置。 我们导入ContextReducer及其初始状态,并将其传递到App.js中的useReducer Hook中。 因为我们现在正在使用上下文,所以我们不会将Context Reducer导入子组件。 状态将在此处的App.js文件中更改,并将仅作为props传递。

import * as ACTIONS from './store/actions/actions';
import * as ContextReducer from './store/reducers/context_reducer';

...

const App = () => {

...

    const [contextState, contextDispatch] = useReducer(ContextReducer.ContextReducer,
                                                       ContextReducer.initialState)
                                                       
    const dispatchContextInc = () => {
      contextDispatch(ACTIONS.CONTEXT_INC)
    }

    const dispatchContextDec = () => {
      contextDispatch(ACTIONS.CONTEXT_DEC)
    }
 ...
 
 
       <div>
      <Context.Provider
          value={{
            //global state with useState
            valueGlobalState_uS: valueGlobal_uS,
            addGlobalValue_uS: () => incrementValueGlobal_uS(),
            decGlobalValue_uS: () => decrementValueGlobal_uS(),

            //global state with useReducer
            valueGlobalState_uR: contextState,
            addGlobalValue_uR: () => dispatchContextInc(),
            decGlobalValue_uR: () => dispatchContextDec()
          }}>
              <HooksContainer1 />
          </Context.Provider>
        </div>
...

We set up our properties in the value prop in the exact same way that we did when we used Context with the useState() hook. The actions are also dispatched in the exact same way as we’ve seen before.

我们以与使用Context和useState()钩子时完全相同的方式在value属性中设置属性。 动作也以与我们之前所见完全相同的方式调度。

Now for our child component:

现在,对于我们的子组件:

...

const HooksContainer1 = () => {
  
 const context = useContext(Context)
 
...

 <button onClick={() => context.addGlobalValue_uR()}> Add Global Value uR </button>
 <button onClick={() => context.decGlobalValue_uR()}> Dec Global Value uR </button>

 <p>Global useReducer Value: {context.valueGlobalState_uR.context_prop1}</p>

As you can see, reading and updating state with useReducer() hook is very similar to the useState() example. We can even use the same context variable we used for useState(), we don't have to initialize another one. To update the state we simply call the property name we defined in the value prop of the provider. This updates the state in App.js. Because we are updating our state in App.js we don't have to import the ContextReducer here in our child component and pass it into the useReducer() hook.

如您所见,使用useReducer()挂钩读取和更新状态与useState()示例非常相似。 我们甚至可以使用与useState()相同的上下文变量,而不必初始化另一个变量。 要更新状态,我们只需调用在提供程序的value属性中定义的属性名称。 这将更新App.js中的状态。 因为我们正在App.js中更新状态,所以我们不必在子组件中导入useReducer()并将其传递到useReducer()挂钩中。

Reading the state is a little bit different. Since valueGlobalState_uR contains our entire state, we have to specify a single property of state which in this case is context_prop1.

读取状态有些不同。 由于valueGlobalState_uR包含我们的整个状态,因此我们必须指定单个state属性,在这种情况下为context_prop1

And this is it! After this you can read and update the state in any component in your app using this same pattern, allowing you to mimic Redux functionality essentially.

就是这样! 之后,您可以使用相同的模式读取和更新应用程序中任何组件的状态,从而从本质上模仿Redux功能。

For a 100% Free Video version of this tutorial and more in-depth React Hooks content see my Udemy course or Youtube playlist:

要获得本教程的100%免费视频版本以及更深入的React Hooks内容,请参阅我的Udemy课程或Youtube播放列表:

https://www.udemy.com/react-hooks-with-react-redux-migration

https://www.udemy.com/react-hooks-with-react-redux-migration

https://www.youtube.com/watch?v=l8ODM-KoDpA&list=PLMc67XEAt-ywplHhDpoj5vakceZNr8S0B

https://www.youtube.com/watch?v=l8ODM-KoDpA&list=PLMc67XEAt-ywplHhDpoj5vakceZNr8S0B

翻译自: https://www.freecodecamp.org/news/how-to-convert-from-react-redux-classes-to-react-hooks-the-easy-way-eca2233e0e7a/

 类似资料: