在 React 中管理子组件的动作
介绍
React 完全是关于单向数据流的。当你将props从父组件传递到子组件时,你实际上是将数据沿层次结构向下流动。在内部,React 利用此流程来控制组件对变化的反应方式。但是,与任何实际应用程序一样,子组件需要将数据沿层次结构向上流动。在本指南中,我们将探讨这方面的基础知识,同时介绍您可能遇到的一些不寻常的情况。
我们正在为产品列表构建一个简单的搜索组件。要求很简单:当用户在搜索中输入一个输入值并点击搜索按钮时,我们会显示与字符串值匹配的项目列表。在本指南中,我们将把应用程序分为三个组件。
App.js
|- SearchInputComponent.js
|- SearchResultComponent.js
当然,我们将把应用程序的全局状态存储在App组件中。子组件所需的任何数据/操作都将沿着层次结构向下传递。以下代码显示了App组件的结构。完整源代码可在Github Repo中找到。
// App.js
import React from 'react';
import SearchInputComponent from './SearchInputComponent';
import SearchResultComponent from './SearchResultComponent';
const listing = [
"Product A",
"React",
"Java",
"Table with chair"
]
class App extends React.Component{
constructor(props){
super(props);
this.state = {
results: listing
}
}
doSearch = (query) => {
console.log(query);
//come complex search goes here
}
render(){
return (
<div>
<h2>Product Search</h2>
<SearchInputComponent />
<SearchResultComponent />
</div>
)
}
}
export default App;
自然流动
首先,我们需要将过滤后的产品列表发送到 SearchResultComponent ,并通过prop将其传递下去。接下来,我们需要确保当用户输入内容时,我们从SearchInputComponent获得正确的更新。因此,我们决定从父组件(App)处理输入状态并在其中创建相应的事件处理程序。要使用事件处理程序,我们再次将其作为 prop 传递下去。
// App.js
// ...
class App extends React.Component{
constructor(props){
super(props);
this.state = {
results: listing,
query: ""
}
}
doSearch = (query) => {
console.log(query);
//come complex search goes here
}
onQueryChange = (txt) => {
this.setState({
query: txt
})
}
render(){
return (
<div>
<h2>Product Search</h2>
<SearchInputComponent
onInputChange = {this.onQueryChange}
query={this.state.query}
/>
<SearchResultComponent
results = {this.state.results}
/>
</div>
)
}
}
export default App;
// SearchResultComponent.js
import React from 'react';
class SearchResultComponent extends React.Component{
render(){
return (
<div>
<ul>
{this.props.results.map((result, i) => (
<li key={i}>{result}</li>
))}
</ul>
</div>
)
}
}
export default SearchResultComponent;
// SearchInputComponent.js
import React from 'react';
class SearchInputComponent extends React.Component{
render(){
return (
<div>
<input
type="text"
value={this.props.query}
onChange={(e) => { this.props.onInputChange(e.target.value) }}
/>
</div>
)
}
}
export default SearchInputComponent;
正如您所看到的,这是自然或单向数据流的理想用法。应用将更新函数onQueryChange()传递给SearchInputComponent ,而SearchInputComponent中的输入又将用户输入数据作为参数发送到更新函数。流始终是单向的。
这种方法适用于实际应用中的大多数用例。如果你发现你的应用程序在遵循该模式方面一直遇到困难,那么你在应用程序中构建状态的方式就存在问题。虽然一开始看起来很麻烦,但从长远来看,花时间习惯这种模式可以节省时间。如果你想深入研究它,React 文档中的Lifting State Up是一个很好的起点。
然而,有时我们会发现需要覆盖这种单向流的情况。这些情况包括:
- 管理焦点、文本选择或媒体播放选项
- 触发动画
- 访问第三方 DOM 库(例如:jQuery)
参考:Refs 和 Dom。
对于这些情况,我们使用Refs。
React Refs
Refs 提供了一种访问在 render 方法中创建的 DOM 节点或 React 元素的方法。
Refs,顾名思义,是对当前 DOM 中 DOM 节点(HTML 或 React)的引用。虽然Ref最常见的用例是与第三方库交互,但我们将重点介绍如何使用它来修改自然数据流之外的子组件。
假设我们不想传递输入更改的事件处理程序,而是希望我们的 App 组件在单击按钮时直接从子组件获取文本输入值。这必然会使指南中的场景过于复杂。因此,我们的想法是在 SearchInputComponent 中创建一个函数,它将返回输入的当前文本值。我们还将使用Ref来获取SearchInputComponent中的输入值。
// SearchInputComponent.js
import React from 'react';
class SearchInputComponent extends React.Component{
constructor(props){
super(props);
this.inputRef = React.createRef();
}
getInputValue(){
return this.inputRef.value;
}
render(){
return (
<div>
<input
type="text"
ref={this.inputRef}
/>
</div>
)
}
}
export default SearchInputComponent;
在上面修改后的SearchInputComponent中,您可以看到我们如何使用Ref来引用输入元素。在getInputValue()函数中,我们访问此元素就像在 Javascript 中访问 DOM 元素一样(请注意,current属性可以访问元素的实例)。这是使用Ref 的一种方法。
现在我们将修改App以从SearchInputComponent收集输入值。
// App.js
// ...
class App extends React.Component{
constructor(props){
super(props);
this.state = {
results: listing,
query: ""
}
this.childRef = React.createRef();
}
doSearch = (query) => {
console.log(query);
//come complex search goes here
}
onSearchClick = () => {
const query = this.childRef.current.getInputValue();
console.log(query);
}
render(){
return (
<div>
<h2>Product Search</h2>
<SearchInputComponent
onInputChange={this.onQueryChange}
ref={this.childRef}
/>
<button
onClick={this.onSearchClick}
>Search</button>
<SearchResultComponent
results = {this.state.results}
/>
</div>
)
}
}
export default App;
在这里,我们为SearchInputComponent创建一个Ref,它是一个 React 组件,而不是 HTML 元素。由于我们正在访问它的当前实例,因此我们也可以访问它的所有方法和实例变量。如果运行上述代码,您可以观察到,单击按钮时,App可以成功访问输入值。
注意:
首先,你可能会对Refs的想法很感兴趣。特别是如果你有原生 JavaScript 背景的话。这感觉就像对元素有更好的控制。但在 React 实践中,强烈不鼓励过度使用Ref。它本质上会覆盖自然数据流,并且可能会产生意想不到的副作用(与第三方 DOM 操作库结合使用时)。另外,需要记住的一件重要事情是,不能使用Refs引用函数组件。由于它们不像 Class 组件那样拥有当前实例,因此你无法引用它们。
结论
在本指南中,我们简要介绍了 React 中单向数据流的概念。然后,我们通过一个简单示例探讨了 React Refs 的必要性。Refs本质上为自然数据流提供了一种旁路机制。虽然它有其用途,但强烈建议不要在应用程序中过度使用Refs 。
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~