发布于 2026-01-06 2 阅读
0

React Hooks:使用 useContext 和 useReducer 提升/传递状态

React Hooks:使用 useContext 和 useReducer 提升/传递状态

这篇文章发表在我的博客上

我遇到过这样的情况:很多子组件和兄弟组件需要共享状态。之前,我通过prop一个方法来传递更新后的状态。但后来,props 的数量不断增加,这让我很头疼。

后来出现了一种context基于全局存储状态并跨组件共享的方法。但即使有了contextAPI,你仍然需要通过 APIrender props来使用全局存储的状态context。你很快就会意识到,你的组件会变得嵌套臃肿、难以维护,而且回头看时会让人感到厌烦。

本文将探讨如何利用 React 的最新hooks概念,以更简洁的代码实现相同的功能。

我们先来构建一个包含一些子组件和同级组件的示例用户界面。

让我们来设计用户界面

前往代码沙箱快速进行实验。请务必先创建一个React代码沙箱。

index.js请将以下内容替换为:

import React from "react";
import ReactDOM from "react-dom";

function App() {
  return (
    <div className="App">
      <h1>Lift up / Pass down state</h1>

      <UserList />
      <AddGenderToUser />
      <AddAgeToUser />
    </div>
  );
}

function UserList() {
  return (
    <ul>
      <li>
        <span>Vimalraj Selvam</span>
        <button type="button">Edit</button>
      </li>

      <li>
        <span>Bhuvaneswari Vimalraj</span>
        <button type="button">Edit</button>
      </li>
    </ul>
  );
}

function AddGenderToUser({ username }) {
  return (
    <div>
      <h2>Add gender to {username}</h2>
      <button type="button">Add Age</button>
    </div>
  );
}

function AddAgeToUser({ username }) {
  return (
    <div>
      <h2>Add Age to {username}</h2>
      <button type="button">Submit</button>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Enter fullscreen mode Exit fullscreen mode

这里我有 3 个子组件,它们都属于父App组件:UserListAddGenderToUserAddAgeToUser

这是一个非常简单的例子,所以不用过多考虑这个应用程序的具体应用场景。

AddGenderToUser我希望仅在点击特定用户的按钮时才显示该组件Edit,并使用所选用户名更新组件的标题。

同样的情况也适用于AddAgeToUser组件,点击组件Add Age中的按钮时也会发生这种情况AddGenderToUser

首先,让我们创建应用程序在未选择任何用户时的初始状态。

const initialState = {
  username: null,
  gender: null,
  age: null
};
Enter fullscreen mode Exit fullscreen mode

然后创建我们的 reducer 方法来执行不同的操作。我能想到的操作有:

  • 更新用户
  • 设置当前用户的性别
  • 设置当前用户的年龄

我们把它放到一个reducer函数里:

const UPDATE_USER = "UPDATE_USER";
const SET_GENDER = "SET_GENDER";
const SET_AGE = "SET_AGE";

function reducer(state, action) {
  switch (action.type) {
    case UPDATE_USER:
      return {
        username: action.username,
        gender: null,
        age: null
      };
    case SET_GENDER:
      return {
        username: state.username,
        gender: action.gender,
        age: null
      };
    case SET_AGE:
      return {
        username: state.username,
        gender: state.gender,
        age: action.age
      };
    default:
      return initialState;
  }
}
Enter fullscreen mode Exit fullscreen mode

我们的 reducer 方法非常简单。它从action参数中获取值,并将其设置为当前状态。

现在让我们在父App组件中使用useReducerReact 的 hook 来使用这个 reducer 函数。这样我们就可以通过它来使用 reducer 的属性context

return让我们在组件语句之前添加以下这行代码App

const [user, dispatch] = React.useReducer(reducer, initialState);
Enter fullscreen mode Exit fullscreen mode

这里user是当前状态,也是dispatch我们触发 reducer 上定义的各种操作的方法。为此,我们必须将该dispatch方法向下传递,并且如果对象发生任何更新state,父对象/父对象的其他子对象也应该知晓。

为了实现上述目标,我们需要利用contextReact 的 API 来存储我们的状态并进行分发。

让我们context用以下代码行进行初始化。这行代码应该放在你的App函数之前(其实位置并不重要)。

const MyContext = React.createContext(null);
Enter fullscreen mode Exit fullscreen mode

我已经将上下文初始化为 null。我们需要将状态和分发放入上下文中。为此,让我们修改App组件,将所有子组件都包裹在context'sprovider 中。更新后的App组件应该如下所示:

<MyContext.Provider value={{ user, dispatch }}>
  <UserList />
  {user.username && <AddGenderToUser />}
  {user.gender && <AddAgeToUser />}
</MyContext.Provider>
Enter fullscreen mode Exit fullscreen mode

太好了,现在我们可以访问user状态以及后续对应的方法dispatch。另外,我还根据user状态属性添加了一些子元素的条件渲染usernamegender

让我们更新组件,使其在特定用户点击按钮时UserList触发相应操作。为此,我们需要从React 的using hook 中获取该方法。UPDATE_USEREditdispatchcontextuseContext

重写后的UserList组件:

function UserList() {
  const { dispatch } = useContext(MyContext);
  return (
    <ul>
      <li>
        <span>Vimalraj Selvam</span>
        <button
          type="button"
          onClick={() => dispatch({ type: UPDATE_USER, username: "Vimalraj" })}
        >
          Edit
        </button>
      </li>

      {/* Removed for brevity */}
    </ul>
  );
}
Enter fullscreen mode Exit fullscreen mode

我们正在分发UPDATE_USERaction 并发送数据username来更新状态的属性。现在,当您点击Edit特定用户的按钮时,可以看到AddGenderToUser组件出现。但是,我们仍然无法在出现的组件中看到用户名。让我们来解决这个问题!

function AddGenderToUser() {
  const { user, dispatch } = useContext(MyContext);

  return (
    <div>
      <h2>Add gender to {user.username}</h2>
      <button
        type="button"
        onClick={() => dispatch({ type: SET_GENDER, gender: "??" })}
      >
        Add Age
      </button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

我们正在获取当前user状态和dispatch方法。我们提取username属性以显示在标题中,并SET_GENDER在点击Add Age按钮时触发操作。

AddAgeToUser你也可以对函数重复相同的操作。

完整版本已在代码沙箱中提供,欢迎在此查看。

编辑升降状态

在代码沙箱中,我对App组件进行了一些更新,以便在年龄更新后显示详细信息。

如果这篇文章对您有帮助,请点赞并分享。

文章来源:https://dev.to/email2vimalraj/react-hooks-lift-up--pass-down-state-using-usecontext-and-usereducer-5ai0