如何处理 ReactJS 中父子组件之间的通信
介绍
在 React 组件之间传递数据看似简单,但有时也会变得有点复杂。虽然将数据从父组件传递到子组件以及从子组件传递数据是一个前期过程,但将数据传递到兄弟组件或从兄弟组件传递数据需要 React 和 JavaScript 提供的特定工具。
本指南的重点将围绕在组件之间传递数据的最直接方法。此外,为了让事情变得真正有趣,我们将通过一个涉及构建僵尸战斗应用程序的示例。
设置
第一步是创建一个新的 React 应用,我们可以使用此存储库。顾名思义,create-react-app存储库将允许我们快速创建一个新的 React 应用。有关指南,请参阅其中的说明。
现在我们已经创建了 React 应用(我们将其命名为zombie-battle),我们可以在src文件夹中创建一个components文件夹。在components文件夹中,我们创建两个文件夹:Zombie和GameMgr,每个文件夹都必须包含一个index.jsx文件,我们将在其中编写组件代码。
接下来,让我们运行一下我们的应用。我们将在浏览器的地址栏中输入localhost:3000,然后检查应用是否启动。请注意,3000 是默认端口号。但是,它可能会有所不同。
现在我们已经确保一切正常,我们可以用以下内容替换现有的App.js :
import React, { Component } from 'react';
import './App.css';
import GameMgr from './components/GameMgr';
class App extends Component {
render() {
return (
<div className="App">
<GameMgr />
</div>
);
}
}
export default App;
由于我们还没有GameMgr组件,我们的应用将无法启动。但是,如果我们将以下代码片段放在相应的index.jsx文件中,它就会正常工作。
父母对孩子
每场僵尸大战都涉及由各自训练师选择的两支对立僵尸。状态正常且速度更快的僵尸将首先发起攻击(当然还有其他因素需要考虑,但为了简单起见,我们坚持基本原则)。每当僵尸的生命值 (HP) 降至 0 时,僵尸就会晕倒,对方僵尸将被宣布为胜利者。另一方面,为了让僵尸发起攻击,其训练师必须选择僵尸先前学到的动作之一。
回到 React 及其组件。将值从父组件传递到子组件很简单;我们只需将值作为子元素的 props 传递即可。为了说明这个概念,请看下面的代码。此代码相当于训练员执行的僵尸选择过程。在这场战斗中,我们派出了一个 Humbug 和一个 Geek 进行战斗。
import React, { Component } from 'react';
import Zombie from '../Zombie';
const Humbug = {
name: 'Humbug',
level: 5,
hp: 20
}
class GameMgr extends Component {
constructor(props) {
super(props);
}
render() {
return(
<div>
<Zombie name={"Geek"} level={5} hp={21}></Zombie>
<Zombie {...Humbug}></Zombie>
</div>
)
}
}
export default GameMgr;
我们可以清楚的看到,Geek 的信息被传递给了GameMgr组件的 render 函数中Zombie组件的第一个实例,props 是分别且连续初始化的。
而 Humbug 的信息是通过 JavaScript 对象(import后定义为常量)通过扩展运算符(ES6)传递的。这样,这个对象的每个键值对都被视为子组件的 prop。
下一步,传递了有关僵尸的必要信息后,我们将提取Zombie组件中的信息。为此,我们需要访问组件的props属性,如下面的代码所示:
import React, { Component } from 'react';
class Zombie extends Component {
render() {
return(
<div>
<h1>Zmb: {this.props.name}</h1>
<ul style={{listStyle: 'none'}}>
<li><span>Level: {this.props.level}</span></li>
<li><span>hp: {this.props.hp}/{this.props.hp}</span></li>
</ul>
</div>
)
}
}
export default Zombie;
访问了组件的 props 属性后,我们现在可以在 render 方法中分别渲染有关每个僵尸的信息。值得一提的是,组件的 props 并不局限于 render,因为我们可以从我们创建的任何其他方法中访问它们。
孩子与父母
为了让僵尸发起攻击,该僵尸的训练员必须从僵尸所学的动作列表中决定所需的动作。一旦训练员选择了僵尸应该使用的动作,就需要将此信息传递给 GameMgr 组件。但是子组件(在我们的例子中是僵尸)如何将该信息传递给父组件(GameMgr)?
实现此目的的简单、直接(且相当流行)方法是传递一个将充当回调的函数。该方法需要接收子进程需要传递给父进程的信息作为参数。
注意下面代码中的getAttack方法。方法签名表明它包含两个参数。稍后会将相同的方法作为子组件的 prop 传递,但不带参数。
import React, { Component } from 'react';
import Zombie from '../Zombie';
const Humbug = {
name: 'Humbug',
level: 5,
hp: 20,
type: 'cpu',
moves: {
move_1: {
name: "Tackle",
power: 4
},
move_2: {
name: "Growl",
power: 0
}
}
}
class GameMgr extends Component {
constructor(props) {
super(props);
this.state = {}
}
getAttack = (zombie, move) => {
let newState = this.state;
newState = {
attack: {
zombie: zombie,
move: move.name
}
}
this.setState(newState);
}
showMessage = () => {
if(this.state.attack) {
return(
<div>
<p>{`${this.state.attack.zombie} uses ${this.state.attack.move}!`}</p>
</div>
)
}
return false;
}
render() {
return(
<div>
<Zombie
type='player'
name={"Geek"}
level={6}
hp={11}
moves={{move_1: {name: "Scratch", power: 5},
move_2: {name: "Leer", power: 0}}}
sendAttack={this.getAttack.bind(this)}>
</Zombie>
<Zombie {...Humbug} sendAttack={this.getAttack.bind(this)}></Zombie>
{ this.showMessage() }
</div>
)
}
}
export default GameMgr;
如果你觉得有点奇怪,请看一下调整后的Zombie组件。
import React, { Component } from 'react';
class Zombie extends Component {
attack = (move) => {
this.props.sendAttack(this.props.name, move);
}
render() {
return(
<div>
<h1>Zmb: {this.props.name}</h1>
<ul style={{listStyle: 'none'}}>
<li><span>Level: {this.props.level}</span></li>
<li><span>hp: {this.props.hp}/{this.props.hp}</span></li>
</ul>
<ul style={{listStyle: 'none'}}>
<li><button onClick={() => this.attack(this.props.moves.move_1)}>{this.props.moves.move_1.name}</button></li>
<li><button onClick={() => this.attack(this.props.moves.move_2)}>{this.props.moves.move_2.name}</button></li>
<li><span>-</span></li>
<li><span>-</span></li>
</ul>
</div>
)
}
}
export default Zombie;
这里我们有一个新的攻击方法,在执行时,会调用之前传递给Zombie组件的一个 prop 的同一个方法。但是,现在该方法包含两个参数:僵尸名称(反过来,它是组件的一个 prop)和所选的攻击对象。
此外,为了让这个过程更加引人入胜,onClick事件的回调方法被设置为下面按钮中的攻击方法。当用户点击按钮选择攻击时,附加的方法(在我们的例子中是攻击)将被调用。
攻击方法的唯一作用是调用GameMgr组件的getAttack 。现在僵尸名称及其训练员选择的攻击通过该函数传递给父组件。
然后,这些信息将存储在GameMgr状态中。一旦状态发生变化,render 方法将再次触发,从而显示方法showMessage生成的消息。由于传递的信息存储在状态中,因此所有使用它的方法都可以访问所述信息(僵尸名称和所选攻击)。
为了简单起见,showMessage方法将仅显示包含僵尸名称及其攻击名称的消息。
因此,我们可以通过将方法传递给子组件并将传递的数据从子组件存储到父组件的状态来执行更复杂的操作,例如,将有关攻击的信息传递给将受到打击的僵尸以及其生命值受到的伤害 - 或者,如果该僵尸很幸运,那么防御者将避免打击的事实。
这就是子组件(Zombie)将数据传递给其父组件(GameMgr)的方式。
结论
在本指南中,我们查看了两个简单示例来说明如何在两个组件之间传递信息。这些示例演示了传递数据的最简单逻辑。但是,根据用例,还有其他方法可以执行此操作。
我们可以发挥想象力,将这两种方式混合起来,以实现兄弟组件之间的通信。然而,如果组件之间没有任何关系,即父组件,我们可以使用 Redux 解决这个问题。此外,在使用 Context 的情况下,它使用中央存储,这样父组件和子组件都可以读取和更新。Redux 或 React-Redux 具有类似的行为,即组件可以从中央存储读取或写入。
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~