入门React——hook组件通信(二)
目录
父组件向子组件通信
- 定义Props: 在父组件中,当你创建子组件实例时,可以通过props属性向子组件传递数据。Props可以是基本数据类型(如字符串、数字),也可以是对象、数组乃至函数。
- 传递Props: 在父组件的JSX代码中,通过标签属性的方式将数据放入子组件标签内。这些属性就是子组件将要接收的props。
- 接收Props: 在子组件内部,通过
this.props
访问由父组件传递过来的数据。你可以直接在render方法中使用这些props值来决定组件的渲染内容。
父组件通过在闭合标签内设置参数直接传递
然后子组件中使用props
对象接收参数,这些参数都已经被挂载在props
对象身上了,只需要对props
对象进行结构操作获取参数即可。
保存运行看效果:子组件是成功的获取到了父组件传递的参数
hook
在讲解子组件向父组件通信通信前我们先要来认识一个概念:React hook。 自React 16.8版本以来,Hooks成为了一个革命性的特性,它允许我们在不编写类组件的情况下使用React的很多特性,比如状态管理(useState)、副作用处理(useEffect)、生命周期等。这极大简化了组件的逻辑,让代码更加直观和易于维护。
为何Hooks在子传父通信中重要
在讲解子组件如何向父组件传递数据时,Hooks,尤其是useState
和useEffect
,起到了核心作用。传统的类组件通信方式虽然可行,但Hooks提供了更为灵活和现代的解决方案。
子组件向父组件通信的基本原理
- 父组件定义状态和回调:利用
useState
,父组件可以声明一个状态变量和一个更新该状态的函数。父组件还会定义一个回调函数,这个函数的任务是更新状态,将来由子组件调用。 - 传递回调至子组件:通过props,父组件将这个回调函数传递给子组件,作为子组件接收的一个属性。
- 子组件调用回调:在子组件中,当需要向父组件发送数据时,通过调用这个回调函数,并将数据作为参数传递给它。
子组件向父组件通信
当我们直接定义传统变量来作为接受子组件传递回来的容器时,不论如何点击按钮都无法生效,
- 不会触发组件重新渲染:React通过比较组件的props和state来决定是否需要重新渲染组件。直接修改普通变量(不在React状态管理中)不会触发组件的重新渲染,因此即使
recvData
的值发生了改变,页面上的显示也不会更新。 - 状态丢失问题:在函数组件中,每次渲染都会创建一个新的函数作用域,这意味着像
recvData
这样的普通变量会在每次渲染时被重新初始化。因此,即使你成功修改了它的值,这个改变也不会持久,下一次渲染时变量又会回到初始值。 - 不符合React单向数据流原则:React鼓励一种单向数据流的编程模型,即数据通常从父组件流向子组件。直接修改变量来传递数据不仅违反了这一原则,还可能导致数据同步和管理上的混乱。
因此我们需要引入hook中的useState
来进行接收传递过来的参数。
在App.js
父组件文件中:
import React, { useState } from 'react'; //导入useState hook
import Component from './component'; //子组件
function App() {
// 使用useState来定义状态
const [recvChildData, setRecvChildData] = useState('暂无数据返回'); //初始状态值
console.log(useState)
// 定义回调函数,并使用setRecvChildData更新状态
const SendChildFunc = (childData) => {
setRecvChildData(childData);
};
return (
<div className="App">
<Component ParentFunc={SendChildFunc}/> //传递给子组件回调函数
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
<code>从子组件接收到的数据: {recvChildData}</code> //接受响应数据
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;
在conponent.js
子组件文件中:
import React, { useState } from 'react';
function Component(props) {
const [localData, setLocalData] = useState('子组件的原始数据'); //初始状态值
const {ParentFunc} = props
// 一个示例方法,模拟数据变化并通知父组件
const sendDataToParent = () => {
// 更新本地状态
setLocalData('这是更新后的数据');
// 调用父组件传递的回调函数,将数据传递给父组件
ParentFunc(localData);
};
return (
<div className="component">
<h1 className='title1'>组件一:</h1>
<p> {/* 添加一个按钮,点击时调用sendDataToParent */}
<button onClick={sendDataToParent}>发送数据给父组件</button>
</p>
</div>
)
}
export default Component
这样便实现了子传父的通信,因为在React中,组件的渲染和更新机制是基于其状态(state)或属性(props)的变化。当状态或属性发生改变时,React会自动重新计算组件的输出(即虚拟DOM),并与之前的虚拟DOM进行对比,找出差异,并仅对实际DOM进行必要的最小更新,这个过程称为Reconciliation(调和)。
兄弟组件间的通信
兄弟组件间的通信通常也是通过它们的共同父组件来间接实现的,这是因为React设计上鼓励单向数据流,即数据从父组件流向子组件。
由于排版比较乱这里先给大家指明一下结构
看我们的实现效果gif动图,如下
父组件
代码
import React, { useState } from 'react';
import './App.css';
import Component from './component';
import Component2 from './component2';
function App() {
const [ChildONE, setChildONE] = useState('父组件获取到组件一的data:空');
const [ChildTWO, setChildTWO] = useState('父组件获取到组件二的data:空');
const delivery1 = (data) => {
setChildONE(data);
}
const delivery2 = (data) => {
setChildTWO(data);
}
return (
<div className="App">
<div className="component-section">
<Component delivery1={delivery1} ChildTWO={ChildTWO} />
<h2 className="component-title">子组件一:{ChildONE}</h2>
</div>
<div className="component-section">
<Component2 delivery2={delivery2} ChildONE={ChildONE} />
<h2 className="component-title">子组件二:{ChildTWO}</h2>
</div>
</div>
);
}
export default App;
子组件一
import './component.css';
import React, { useState } from 'react';
function Component2(props) {
const { delivery1, ChildTWO } = props;
const [localData, setLocalData] = useState([
'组件一:data1', '组件一:data2', '组件一:data3', '组件一:data4', '组件一:data5', '组件一:data6', '组件一:data7'
]);
const [useTimer, setUseTimer] = useState(0);
const sendDataToBrother = () => {
delivery1(localData[useTimer]);
setUseTimer((prevTimer) => (prevTimer + 1) % localData.length);
}
return (
<div className="component">
<button onClick={sendDataToBrother}>组件一传递出去的data数据</button>
<h1 className='title1'>组件一</h1>
<p>
</p>
<p>这是组件一,收到组件二的数据:{ChildTWO}</p>
</div>
);
}
export default Component2;
子组件二
import './component.css';
import React, { useState } from 'react';
function Component(props) {
const { delivery2, ChildONE } = props;
const [localData, setLocalData] = useState([
'组件二:data1', '组件二:data2', '组件二:data3', '组件二:data4', '组件二:data5', '组件二:data6', '组件二:data7'
]);
const [useTimer, setUseTimer] = useState(0);
const sendDataToBrother = () => {
delivery2(localData[useTimer]);
setUseTimer((prevTimer) => (prevTimer + 1) % localData.length);
}
return (
<div className="component">
<h1 className='title1'>组件二</h1>
<p>
<button onClick={sendDataToBrother}>组件二的传递出去的data数据</button>
</p>
<p>这是组件二,收到组件一的数据:{ChildONE}</p>
</div>
);
}
export default Component;
子组件不应直接修改非本组件管理的数据,而是通过调用父组件传递下来的回调方法来请求数据更新,这是React倡导的单向数据流原则。这样做不仅保持了组件的独立性和职责分离,还确保了状态管理的可预测性和可维护性,使应用结构更加清晰和有序。
在React中,数据流通常是单向的:从父组件向下传递给子组件,而子组件通过调用父组件提供的回调函数来请求数据更新或状态改变,这种模式有助于维护数据一致性,避免不必要的复杂状态同步问题。
转载自:https://juejin.cn/post/7386873037109280778