React-02-组件通信&useEffect
父子通信
父子通信-父传子
实现步骤:
- 父组件传递数据 - 在子组件标签上绑定属性
- 子组件接收数据 - 子组件通过
props参数接收数据
// 2.子组件通过prop对象拿到父组件数据
function Son(prop) {
return (
<div style={{ background: "pink", width: "300px", height: "100px" }}>
我是子组件, 父组件的数据如下
{/* 3.通过prop属性拿到具体数据 */}
<div> {prop.fData}</div>
</div>
);
}
function App() {
const name = "父组件数据";
return (
<div className="App">
{/* 1.父组件传递数据 */}
<Son fData={name}></Son>
</div>
);
}
export default App;
props说明
function Son(prop) {
// 打印所有 prop 对象
console.log(prop)
return (
<div style={{ background: "pink", width: "300px", height: "100px" }}>
我是子组件, 父组件的数据如下
{/* 3.通过prop拿到具体属性 */}
<div> {prop.name}</div>
<div> {prop.age}</div>
<div> {prop.hobby}</div>
</div>
);
}
function App() {
const name = "张三";
const age = 18;
const hobby = ["跑步", "游泳"];
const obj = {"obj": "对象"}
const action = () => console.log("study");
return (
<div className="App">
{/* 1.父组件传递数据 */}
<Son name={name} age={age} hobby={hobby} obj={obj} action={action}></Son>
</div>
);
}
export default App;
控制台
{action: () => console.log("study")
age: 18
hobby: (2) ['跑步', '游泳']
name: "张三"
obj: {obj: '对象'}
[[Prototype]]: Object
特殊的prop-children
function Son(prop) {
// 打印所有 prop 对象
console.log(prop);
return (
<div style={{ background: "pink", width: "300px", height: "100px" }}>
我是子组件, 父组件的数据如下
<br />
{/* 子组件可以从 prop.children 中去取 */}
{prop.children}
</div>
);
}
function App() {
return (
<div className="App">
<Son>
{/* 特殊:如果父组件嵌套了一个组件传进来 */}
<span>子组件</span>
</Son>
</div>
);
}
export default App;
父子通信-子传父
核心思路:在子组件中调用父组件中的函数并传递参数
子传父示例,并且实时渲染展示
- 在父组件中定义一个接收数据的函数
- 将函数传递到子组件,这里prop名有规范(以on开头)
- 子组件在 prop 中拿到父组件函数
- 子组件通过回调函数将数据发送给父组件
- 父组件可以通过 useState 绑定子组件发送的消息,并且加以渲染
import { useState } from "react";
// 3. 子组件在 prop 中拿到父组件函数
function Son({onGetMsg}) {
const msg = "我发送了一条消息";
return (
<div style={{ background: "pink", width: "300px", height: "100px" }}>
我是子组件<br/>
{/* 4. 子组件通过回调函数将数据发送给父组件 */}
<button onClick={() => onGetMsg(msg)}>发送消息给父组件</button>
</div>
);
}
function App() {
// 5. 父组件可以通过 useState 绑定子组件发送的消息,并且加以渲染
const [childMsg, setChildMsg] = useState('')
// 1.在父组件中定义一个接收数据的函数
const getMsg = (msg) => {
console.log(msg)
setChildMsg(msg)
}
return (
<div className="App">
{childMsg}
{/* 2. 将函数传递到子组件,这里prop名有规范(以on开头) */}
<Son onGetMsg={getMsg}></Son>
</div>
);
}
export default App;
父子通信-兄弟组件通信
实现思路: 借助
状态提升机制,通过共同的父组件进行兄弟之间的数据传递
- A组件先通过子传父的方式把数据传递给父组件App
- App拿到数据之后通过父传子的方式再传递给B组件
import { useState } from "react";
// B组件拿到数据并且使用
function ChildB({ msg }) {
return (
<div style={{ background: "yellow", width: "300px" }}>
B: 我拿到了 {msg}
</div>
);
}
function ChildA({ onGetMsg }) {
const msg = "我是A";
// 3. 在A组件中调用回调函数,并且将数据传递给父组件
return (
<div style={{ background: "pink", width: "300px" }}>
<button onClick={() => onGetMsg(msg)}>B</button>
</div>
);
}
// App -> index.js -> public/index.html(root)
function App() {
// 3. 定义B组件的State
const [aMsg, setAMsg] = useState("");
// 1. 定义A组件的回调函数
const getMsg = (msg) => {
console.log(msg);
setAMsg(msg);
};
return (
<div className="App">
{/* 2. 将回调函数传递给子A组件 */}
<ChildA onGetMsg={getMsg}></ChildA>
{/* 4. 将State传递给B组件 */}
<ChildB msg={aMsg}></ChildB>
</div>
);
}
export default App;
React副作用管理-useEffect
概念
useEffect 是一个 React Hook 函数,用于在 React 组件中创建不是由事件引起而是由渲染本身引起的操作(副作用), 比 如发送 AJAX 请求,更改 DOM 等等
useEffect 副作用函数的执行时机存在多种情况,根据传入依赖项的不同,会有不同的执行表现
| 依赖项 | 副作用功函数的执行时机 |
|---|---|
| 没有依赖项 | 组件初始渲染 + 组件更新时执行 |
| 空数组依赖 | 只在初始渲染时执行一次 |
| 添加特定依赖项 | 组件初始渲染 + 依赖项变化时执行 |
代码示例
import { useEffect, useState } from "react";
function App() {
const [count, setCount] = useState(0);
// 当组件加载完成后会调用 useEffect
useEffect(() => {
async function getMsg() {
console.log("我执行了")
}
getMsg();
// }); // 1. 组件每次更新都会执行一次
// }, []); // 2. 只会在首次渲染执行一次
}, [count]); // 3. 在指定数据发生变化时执行
const click = () => console.log("我执行了");
const [msg, setMsg] = useState("");
return (
<div className="App">
<button onClick={() => setCount(count + 1)}>{count}</button>
</div>
);
}
export default App;
清除副作用
概念:在useEffect中编写的由渲染本身引起的对接组件外部的操作,社区也经常把它叫做副作用操作,比如在useEffect中开启了一个定时器,我们想在组件卸载时把这个定时器再清理掉,这个过程就是清理副作用
通过条件渲染模拟组件卸载
import { useEffect, useState } from "react";
function Child() {
useEffect(() => {
const timer = setInterval(() => {
console.log("定时器执行中");
}, 1000);
return () => {
// 清除副作用(组件卸载时)
clearInterval(timer)
}
});
return <button>我是子组件</button>;
}
function App() {
const [show, setShow] = useState(true);
return (
<div className="App">
<button onClick={() => setShow(!show)}>点我 装载/卸载</button>
{show && <Child />}
</div>
);
}
export default App;
自定义Hook
基于上一个demo,可以发现定时器的装卸在是靠如下的逻辑来实现
const [show, setShow] = useState(true);
return (
<div className="App">
<button onClick={() => setShow(!show)}>点我 装载/卸载</button>
{show && <Child />}
</div>
);
下面可以通过组件封装进行优化:
- 声明一个以use打头的函数
- 在函数体内封装可复用的逻辑(只要是可复用的逻辑)
- 把组件中用到的状态或者回调return出去(以对象或者数组)
- 在哪个组件中要用到这个逻辑,就执行这个函数,结构出来状态和回调进行使用
import { useEffect, useState } from "react";
function useToggle() {
// 可复用的逻辑代码
const [value, setValue] = useState(true);
const toggle = () => setValue(!value)
return {value, toggle}
}
function App() {
const {value, toggle} = useToggle()
return (
<div className="App">
<button onClick={toggle}>点我装/卸载</button>
{value && <div>我是一个标签</div>}
</div>
);
}
export default App;
