React 中的组件间通信
介绍
React是一个基于组件的 UI 库。当 UI 被拆分成小而集中的组件时,它们可以完成一项工作,并且做得很好。但为了将系统构建成可以完成一项有趣任务的东西,需要多个组件。这些组件通常需要协同工作,因此必须能够相互通信。数据必须在它们之间流动。
React 组件的层次结构模仿了它们用于创建的 DOM 树层次结构。层次结构中有一些组件较高(父组件),有一些组件较低(子组件)。让我们来看看 React 在组件之间实现的定向通信和数据流。
从父母到孩子,用道具
最简单的数据流方向是沿着层次结构向下,从父级到子级。React 实现此目的的机制称为props 。React 组件是一个接收名为props的参数的函数。Props 是一袋数据,是一个可以包含任意数量字段的对象。
如果父组件想要将数据提供给子组件,只需通过props传递数据即可。假设我们有一个BookList组件,其中包含书籍列表的数据。当它在渲染时遍历书籍列表时,它希望将列表中每本书的详细信息传递给子Book组件。它可以通过props来实现这一点。这些props作为 JSX 中的属性传递给子组件:
function BookList() {
const list = [
{ title: 'A Christmas Carol', author: 'Charles Dickens' },
{ title: 'The Mansion', author: 'Henry Van Dyke' },
// ...
]
return (
<ul>
{list.map((book, i) => <Book title={book.title} author={book.author} key={i} />)}
</ul>
)
}
然后Book组件可以接收并使用props参数中包含的那些字段到其函数中:
function Book(props) {
return (
<li>
<h2>{props.title</h2>
<div>{props.author}</div>
</li>
)
}
只要有意义,就支持这种最简单的数据传递形式。
然而,这里有一个限制,因为props是不可变的。传递到props中的数据永远不应该改变。但是子组件如何与父组件通信呢?一个答案是回调。
通过回调从子节点到父节点
为了让子级与父级对话(我知道这是不可接受的!),它必须首先获得一种与父级进行通信的机制。正如我们所了解的,父级通过props将数据传递给子级。可以将函数类型的“特殊” prop传递给子级。在发生相关事件(例如,用户交互)时,子级可以调用此函数作为回调。
假设可以通过BookTitle组件编辑一本书:
function BookTitle(props) {
return (
<label>
Title:
<input onChange={props.onTitleChange} value={props.title} />
</label>
)
}
它在props中接收一个onTitleChange函数,该函数由其父级发送。它将此函数绑定到<input />字段上的onChange事件。当输入发生变化时,它将调用onTitleChange回调,并传递 change事件对象。
因为父级BookEditForm引用了此函数,所以它可以接收传递给该函数的参数:
import React, { useState } from 'react'
function BookEditForm(props) {
const [title, setTitle] = useState(props.book.title)
function handleTitleChange(evt) {
setTitle(evt.target.value)
}
return (
<form>
<BookTitle onTitleChange={handleTitleChange} title={title} />
</form>
)
}
在这种情况下,父组件传递了handleTitleChange ,并且在调用它时,它会根据evt.target.value的值设置内部状态——该值是来自子组件的回调参数。
然而,在某些情况下,通过props发送数据可能不是组件间通信的最佳选择。对于这些情况,React 提供了一种称为 context 的机制。
从父母到孩子,了解背景情况
如果我们希望某些东西在全局可用(在层次结构的多个组件和级别中),则props传递可能会很麻烦。想想我们可能希望广播给所有子组件的某些数据,无论它们在哪里,它们都会对其作出反应,例如主题数据。我们可以定义一个主题上下文,在顶层提供,然后在任何需要它的子组件中使用它,而不是将主题props传递给层次结构中的树或子树下的每个组件。
假设我们回到BookList中书籍列表的示例,并在其上方有一个名为BookPage 的父组件。在该组件中,我们可以为主题提供上下文:
const ThemeContext = React.createContext('dark')
function BookPage() {
return (
<ThemeContext.Provider value="light">
<BookList />
</ThemeContext.Provider>
)
}
ThemeContext只需创建一次,因此是在组件函数之外创建的。它默认使用“ dark”作为后备主题名称。上下文对象包含一个Provider函数,我们将渲染的子组件包装在其中。我们可以指定一个值来覆盖默认主题。在这里,我们说我们的BookPage将始终显示“light”主题。还要注意,BookList不会收到任何主题道具。我们可以保持其实现不变。但假设我们希望我们的Book组件响应主题。我们可以将其调整为如下内容:
import React, { useContext } from 'react'
function Book(props) {
const theme = useContext(ThemeContext)
const styles = {
dark: { background: 'black', color: 'white' },
light: { background: 'white', color: 'black' }
}
return (
<li style={styles[theme]}>
<h2>{props.title</h2>
<div>{props.author}</div>
</li>
)
}
Book需要访问在BookPage旁边创建的ThemeContext对象,并且该上下文对象被提供给useContext钩子。从那里,我们创建一个简单的样式图,并根据theme的值选择适当的样式。根据BookPage中提供的theme值,我们将在此处显示黑色背景样式。
上下文的特殊之处在于,主题不是来自于道具,而只是因为父组件将其提供给所有使用它的子组件才可用。
与大多数全局代码模式一样,请谨慎使用上下文。它会在组件之间产生耦合,从而导致代码和关系的可重用性降低,或者导致组件之间的联系变得不那么清晰。
如果上下文值是一个回调函数,我们可以看到它也用于子与父之间的通信。
使用非 React 选项进行横向移动
正如我们所见,React 提供了在组件层次结构中上下通信的模式。由于所有组件都存在于此层次结构中,因此这是自然而有效的。
但是,如果我们想要“横向”通信,即数据不是来自父组件或从子组件返回,该怎么办?React 可以通过将数据向上传递到层次结构,然后通过不同的路径返回到兄弟组件来实现这一点。但是,如果我们真的希望数据不通过父组件或子组件关系流动,我们就必须跳出 React。
当我们脱离 React 时,数据将不再来自props、context 或 React 传递的回调。它将来自原始 JavaScript 类型的来源,例如我们导入的模块或我们观察到的 JavaScript 对象。
有些库已经将处理 React 之外的数据流的模式形式化,但与 React 配合得很好。Redux就是一个常见的例子,其中单个状态树在组件层次结构之外维护,但旨在轻松连接到您的组件,从而允许横向访问数据。
如果由于某种原因父子之间的沟通没有意义,请记住这组非 React 选项。
结论
React's mechanisms for communicating between components are simple and effective. props allow data to flow down the component hierarchy from parent to child. When a child wants to communicate back up to a parent, a callback function is passed through props. Context provides additional convenience and makes interesting code patterns possible through globally providing data across the component tree hierarchy. There are also additional libraries and patterns that integrate well with React to communicate across components.
Experiment with all these communication patterns. Then stick with the simplest, most natural option to fit the problem you're solving. The data must flow.
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~