使用 React Hooks 将数据从子组件传递到父组件

2025-06-07

使用 React Hooks 将数据从子组件传递到父组件

最近我接到一个挑战,要用 Hooks 实现一个简单的 React 身份验证系统。有人给我推荐了这篇文章作为示例,我发现用一行代码控制整个应用的身份验证是一个很有意思的方法。在这个演示中,我希望用户能够输入用户名来“登录”,然后网站会显示“hello, [username]”来欢迎用户。

总体布局

这个演示背后的基本思路是在根父组件中设置一个单一状态,用于保存用户身份验证信息。根据用户是否通过身份验证,加载不同版本的网站。

const App = () => {
  const [user, setUser] = useState(null);

  return user ? <AuthWebsite/> : <NoAuthWebsite/>
};
Enter fullscreen mode Exit fullscreen mode

很简单,对吧?但是状态如何更新呢?必须有一种方法将用户信息传递到组件树的上层,以便更新存储在[用户]状态中的信息。

从子级向父级传递数据

等等,单向数据流不是 React 的核心设计理念吗?没错。而且我们不应该使用最常用的数据传递方法——props——将任何东西传递到组件树的上层。然而,我们实际上可以在父组件中设计函数,并将它们传递到下层组件树。我们可以将变量或任何其他数据作为参数传递到子组件中的函数中。

将用户名传递回组件树的子组件如下所示:

const NoAuthWebsite = ({ login }) => {
  const [userName, setUserName] = useState("");

  return (
    <form onSubmit={() => login(userName)}>
      <input
        placeholder="username"
        required="required"
        onChange={e => setUserName(e.target.value)}
        value={userName}
      />
      <button type="submit">
        submit
      </button>
    </form>
  );
};
Enter fullscreen mode Exit fullscreen mode

(这里的状态仅用于在表单中存储用户响应)

上面,login 被当作 NoAuthWebsite 组件的一个 prop。当用户加载网站时,该组件会向用户展示一个表单,要求用户填写用户名。用户名会被作为参数提交给 login 函数,并作为 prop 传递下去。现在,让我们在上面编写的父组件中添加 login() 函数,并将其传递下去:

const App = () => {
  const [user, setUser] = useState(null);

  return user ? (
    <AuthWebsite logout={() => setUser(null)} user={user} />
  ) : (
    <NoAuthWebsite login={username => setUser(username)} />
  );
};
Enter fullscreen mode Exit fullscreen mode

现在,我们已经将用户提交的用户名设置为 [user] 状态。如果该状态存在,我们将加载网站的授权版本。如果您注意到,我们将一个注销函数传递给了 AuthWebsite 组件,以便用户可以注销,网站可以返回到其默认(未授权)状态。不过,在这种情况下,我们不需要将子组件传递到组件树中,因为只需将 User 设置为 null 即可。我们现在可以构建授权网站组件,并使其能够使用用户名来欢迎用户:

const AuthWebsite = ({ logout, user }) => {
  return (
    <div>
      <h2>Hello, {user}</h2>
      <div className="logout_button" onClick={() => logout()}>
        logout
      </div>
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

就这样!一个简单的 Web 应用身份验证演示,通过函数将数据从子组件传递到父组件!

向我们的应用程序添加一个更有趣的例子

迎接用户的登录表单有点枯燥。让我们重新运用这些概念,让它更有趣一些,做一个Modal (模态框),或者一个弹出式的覆盖卡片,用户可以选择提交或点击退出。这些模态窗口在网络上随处可见,几乎可以用于任何用途。

实现此效果非常简单,只需使用三元组来切换 CSS 即可。使用 CSS,您可以使用“display”属性控制 HTML 元素是否显示。与第一个示例中的操作类似,二进制状态可以控制组件的 className。然后,可以将切换状态的函数传递给覆盖组件本身。

const NoAuthWebsite = () => {
  const [overlay, setOverlay] = useState(false);

  return (
    <div className="flex_column">
      <div className={overlay ? "overlay_shown" : "overlay_hidden"}>
        <LoginOverlay
          removeOverlay={() => setOverlay(false)}
        />
      </div>
      <h2>You are not Authorized</h2>
      <div className="login_button" onClick={() => setOverlay(true)}>
        Login
      </div>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode
.overlay_shown {
  opacity: 1;
}

.overlay_hidden {
  display: none;
  opacity: 0;
}
Enter fullscreen mode Exit fullscreen mode

stopPropagation() 用于阻止 overlay_background div 上的 onClick 函数传播到其所有子级。如果没有它,点击模态框的任意位置都会导致 onClick 函数触发,并移除模态框。

const stopProp = e => {
  e.stopPropagation()
}

const LoginOverlay = ({ removeOverlay }) => {
  const [userName, setUserName] = useState("")

  return (
    <div className="overlay_background" onClick={e => removeOverlay()}>
      <div className="overlay_card" onClick={()) => stopProp(e)}>
        <form onSubmit={e => removeOverlay()}>
          <input
            placeholder="username"
            required="required"
            onChange={e => setUserName(e.target.value)}
            value={userName}
          />
          <button className="form_submit" type="submit">
            submit
          </button>
        </form>
      </div>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

就这样!把它们连接在一起,并添加一些可视化显示来查看数据流路径后,您可以在这里查看完整的现场演示,或者在这里查看源代码

结论

使用函数是将数据向上传递到组件树的绝佳方式。它可以用于多种用途,最显著的是基于用户在子组件中的交互/输入进行渲染。将此技巧与 React Hooks 结合使用,有助于编写美观且易于维护的代码,因为通过函数式组件和函数本身,逻辑流程更容易理解。

如果您有任何问题、意见、问题,或者只是想聊天,请随时给我留言。

文章来源:https://dev.to/pnkfluffy/passing-data-from-child-to-parent-with-react-hooks-1ji3
PREV
hack.chat 网页聊天 hack.chat 安装 贡献 致谢 语法高亮介绍 #14
NEXT
十分钟内从 Java 8 升级到 Java 15 函数式编程 (Java 8) Streams (Java 8) Optional (Java 8) JShell (Java 9) 不可变集合的工厂方法 (Java 9) 使用 var 进行类型推断 (Java 10) 单源文件启动 (Java 11) Switch 表达式 (Java 12) 多行字符串 (Java 13) 数据类:record (Java 14) 不带强制类型转换的实例 (Java 14) 密封类 (Java 15) 额外奖励:从 Java 8 开始更新许可条款 总结