1 React 组件传值(props 传递)
这是 React 组件传值(props 传递)相关知识,核心是 判断哪些数据该由父组件 <App/> 向下传递给子组件,思路是看 “多个子组件是否共用同一数据”,以下用大白话拆解:
1. 核心逻辑:“谁需要,就传递谁”
- props(属性):父组件给子组件传数据的方式,子组件通过
props
接收父组件传递的信息 。 - 判断标准:如果多个子组件都要用同一份数据,这份数据就该由最上层的
<App/>
作为props
传递下去;反之,若只有单个子组件用,就没必要从<App/>
传,在子组件自己内部存状态(state)就行 。
2. 图中案例分析
- 数据分类:
blocked users
(屏蔽用户列表)、following
(关注列表)、followers
(粉丝列表):这些数据,多个子组件(Navbar、Feed、Trending、Suggestions 等)都要用 。比如<Navbar/>
可能要根据blocked users
控制显示,<Suggestions/>
可能要用following
做推荐逻辑 。所以它们得由<App/>
作为props
传递给需要的子组件 。list of tweets
(推文列表):只有<Feed/>
及其子组件<Tweet/>
用 ,其他子组件用不上。所以不用从<App/>
传递,直接在<Feed/>
内部用state
存就行,自己玩自己的 。
总结一下就是:父组件 <App/>
要把 “多个子组件都会用到” 的数据,通过 props 传递下去;只给单个子组件用的数据,让子组件自己内部存着玩 。
这样能避免数据到处乱传,让组件传值更清晰,也方便维护。
2 React 里 “兄弟组件无法直接传值”
1. 核心概念:“状态(State)” 和 “兄弟组件”
- State(状态):组件里存的数据,像图里
<Parent/>
的blockedUsers
(被屏蔽用户列表),就是组件自己的状态 。 - 兄弟组件:同一个父组件(这里是
<Parent/>
)下面的子组件,比如<Profile/>
和<Feed/>
,它们是 “兄弟” 关系 。
2. 关键规则:“兄弟之间,不能直接传数据”
React 的规则是 兄弟组件没法直接互相传递信息 。
比如图里,<Profile/>
想用 <Feed/>
的数据,或者反过来,不能直接传!得绕个弯:
数据得先存在它们的共同父组件(<Parent/>
) 里,再由父组件通过 props
(属性),分别传递给这两个兄弟组件 。
像图里这样:blockedUsers
存在 <Parent/>
里,然后父组件把它作为 props
,分别传给 <Profile/>
和 <Feed/>
,而不是让兄弟俩直接互相传 。
总结一下就是:React 里兄弟组件不能直接交流传数据,得靠它们共同的父组件当 “中间商”,父组件存数据,再分别传给兄弟组件 。这样设计是为了让组件传值有清晰的流程,避免数据乱传不好维护~(以后写组件碰到兄弟传值,就记得找父组件当中间人哈)
3 React "单向数据流"
我由前面两个PPT的思考:
所以意思就是数据全部在parent 组件里面,如果child子组件需要这个数据,那就从父组件里面props?
具体分析下:
对啦!你理解得很到位~简单说就是 “数据存在共同父组件,子组件要用就找父组件通过 props 传” ,这是 React 里很重要的 “单向数据流” 思路,再给你补点细节加固理解:
1. 核心逻辑:“父存数据,子靠 props 拿”
- 父组件(比如
<Parent/>
)负责 “存状态(state)” ,像用户信息、列表数据这些,都存在父组件里 。 - 子组件(不管是兄弟、嵌套多深的子组件)“要用数据,就找父组件要” ,父组件通过
props
把数据传递下去 。
比如图里,<Profile/>
和 <Feed/>
要用 blockedUsers
,就得让父组件 <Parent/>
用 props
传给它们,而不是兄弟之间直接传。
2. 为啥要这么设计?
- “好管理”:数据只存在父组件,修改数据也只在父组件改,子组件只用 “接收和展示” 。要是数据乱传,改数据的时候都不知道在哪改、会不会影响其他组件,容易搞乱 。
- “流程清晰”:父 → 子 单向传值,数据流能 “顺着看明白” 。比如调试的时候,知道数据从父组件来,排查问题也方便 。
3. 延伸场景:“多层嵌套咋办?”
要是子组件嵌套很深(比如父 → 子 → 孙 → 曾孙),总不能让父组件一直传 props 到曾孙吧?这时候可以用 “上下文(Context)” 或者 “状态管理库(Redux、 Zustand 等)” 简化传值,但核心思路还是 “数据存在统一地方,需要的组件去拿” ~
简单说就是:父组件当 “大仓库” 存数据,子组件要啥,父组件用 props 发。这样 React 里的数据流动才清晰,不容易乱套 ,你悟到 React 传值的 “套路” 啦,继续加油!
你理解得完全正确,前面两个 PPT 讲的核心就是这个意思:
- 数据应该集中存放在共同的父组件中(作为 state)
- 子组件需要使用这些数据时,通过 props 从父组件获取
- 兄弟组件之间不能直接传值,必须通过它们的共同父组件作为 "中转站"
这种模式在 React 中叫做 "单向数据流",是 React 组件设计的核心原则之一。简单说就是 "数据从上往下流,修改从下往上提",这样能让整个应用的数据流动清晰可追踪,方便维护和调试。
你总结的 "数据全部在 parent 组件里面,如果 child 子组件需要这个数据,那就从父组件里面 props",完美概括了这个核心思想,特别精准!
4 判断需不需要传递
【我现在的问题就是,我根本不知道右边这个框框里面的数据需不需要传递下去,我根本不知道这个网页怎么设计可以达到效果?】
其实判断需不需要传递,核心就看 “子组件要不要用这些数据来实现功能” 。结合这张图,咱们一步步理清楚:
1. 先明确需求:<Suggestions/>
要实现啥功能?
看右侧示例,<Suggestions/>
是 “推荐关注” 模块(Who to follow),每个推荐的 <Profile/>
里有 “Follow” 按钮。
按钮状态(能不能点、是不是已关注),需要 following
数据 来判断:如果用户已经关注(在 following
列表里),按钮可能变成 “已关注”;没关注就显示 “Follow” 。
2. 逐个看右侧数据要不要传:
blocked users
(屏蔽用户):<Suggestions/>
的子组件<Profile/>
用不上这个。推荐关注模块不需要知道用户屏蔽了谁,所以不用传 。list of tweets
(推文列表):推荐关注又不是看推文,<Profile/>
用不到,不用传 。followers
(粉丝列表):“推荐关注” 功能不需要粉丝数据判断按钮状态,不用传 。following
(关注列表):前面说过,要判断 “Follow” 按钮状态(已关注 / 未关注),必须用这个数据,所以需要传 。
3. 总结 “传值判断逻辑”(以后碰到类似问题套用):
- 明确子组件功能:先想子组件要干啥,需要哪些数据支撑(比如这里
<Profile/>
要控制按钮状态)。 - 匹配父组件数据:看父组件(这里是
<Suggestions/>
)有的数据里,哪些能满足子组件功能需求。 - 判断是否传递:子组件需要的,就传;用不上的,别传(传了也是多余,还可能让数据 flow 混乱)。
简单说就是 “子组件功能决定传什么” 。比如做 “购物车组件”,子组件要显示商品总价,那父组件的 “商品列表 + 价格” 数据就得传;要是子组件只负责 “展示商品图片”,可能就不用传价格数据 。多结合实际功能需求想,慢慢就懂啦,你已经在靠近 React 传值的 “灵魂” 啦。
5 setState 是异步操作
1. 先看代码想干啥
有个状态 persons
,初始是空数组 []
。
testingStuff
函数里,先用 setPersons
给 persons
加个元素 'me'
,然后 console.log(persons)
想看看更新后的数据 。
2. 关键:setState(这里是 setPersons )是“异步”的
- “异步” 意思:调用
setPersons
后,React 不会 “立刻” 把persons
改成新值,而是先记下来 “要更新”,等合适时机(比如当前函数执行完、批量更新优化)才真正修改状态 。 - 所以代码执行顺序是这样的:
- 调用
setPersons
,告诉 React “我要把persons
改成[...persons, 'me']
”(但还没真正改)。 - 接着执行
console.log(persons)
,这时候因为setPersons
是异步的,persons
还没被真正更新,所以打印的是原来的空数组[]
。
- 调用
3. 为啥会这样设计?
React 为了 “性能优化” 这么干的。如果每次调用 setState
都立刻更新状态、重新渲染组件,频繁操作会很卡。所以 React 会把多个 setState
攒一攒,批量更新,减少渲染次数 。
4. 那怎么正确拿到更新后的状态?
如果想在状态更新后执行代码(比如打印新的 persons
),得用 setState
的回调函数 或者 useEffect(函数组件里) :
类组件(用 setState
回调):
jsx
testingStuff = () => {
setPersons([...persons, "me"], () => {
// 状态更新完,才会执行这里
console.log(persons); // 能拿到 ['me']
});
};
函数组件(用 useEffect
监听状态变化):
jsx
const [persons, setPersons] = useState([]);
const testingStuff = () => {
setPersons([...persons, "me"]);
};
// persons 变化时,执行这个回调
useEffect(() => {
console.log(persons);
}, [persons]);
总结一下:React 里修改状态(setState)是异步的,想拿到更新后的状态,不能直接在 set 之后立刻访问,得用回调或者 useEffect 等 “状态更新后” 的时机 。刚开始可能觉得绕,但记住 “异步更新” 这个特点,多写几次就懂啦,别慌~
6 什么意思?什么回调?
“回调函数” 这个概念,刚开始接触确实有点抽象,我用最接地气的方式给你讲明白,保证听完就会用 👇
1. 先理解 “回调函数” 是啥(生活化比喻)
你可以把 “回调函数” 想成 “预约任务” :
- 比如你点外卖,下单后(相当于调用
setPersons
),外卖不会 “立刻送到”(状态不会立刻更新)。 - 但你可以 “预约” 一个动作:“等外卖送到了(状态更新完成),给我发消息(执行回调函数里的代码)” 。
这里的 “发消息” 就是回调函数干的事儿 —— 等某个异步操作完成后,再执行的函数 。
2. 回到 React 代码里的 “回调”
在类组件的 setState
(比如 setPersons
)里,第二个参数可以传一个函数,这个函数就是 “回调函数” ,专门用来 “等状态更新完成后,再执行” 。
看代码:
jsx
testingStuff = () => {
// 1. 调用 setPersons,告诉 React 要更新状态(异步操作,不会立刻完成)
setPersons([...persons, "me"], () => {
// 3. 状态更新完成后,才会执行这里的代码
console.log('状态更新完啦,新的 persons 是:', persons);
});
// 2. 这里的代码会先执行(此时状态还没更新)
console.log('我先执行,但这时候 persons 还没更新');
};
- 执行顺序:
- 调用
setPersons
,触发状态更新(异步,先记下来要更新)。 - 先执行
setPersons
后面的代码(比如上面的console.log
)。 - 等状态真正更新完成后,才执行回调函数里的代码(打印更新后的
persons
)。
- 调用
3. 为啥需要回调?(解决你的痛点)
因为 setState
是异步的,如果你想 “确保拿到最新的状态” ,就必须用回调函数。
比如最开始的例子:
- 直接
console.log(persons)
会拿到旧值(因为状态还没更新)。 - 用回调函数,就能拿到更新后的新值(状态更新完才执行)。
4. 函数组件里的替代方案(useEffect
)
如果你用的是函数组件( hooks ),没有 setState
回调了,就用 useEffect
监听状态变化:
jsx
import { useState, useEffect } from 'react';
function MyComponent() {
const [persons, setPersons] = useState([]);
const testingStuff = () => {
// 触发状态更新(异步)
setPersons([...persons, "me"]);
};
// 监听 persons 变化,变化后执行回调
useEffect(() => {
console.log('persons 更新啦,新值是:', persons);
}, [persons]);
return (
<div>
<button onClick={testingStuff}>更新 persons</button>
</div>
);
}
- 这里的
useEffect
就相当于类组件里setState
的回调:当persons
状态变化后,才执行里面的代码 。
5. 总结一下 “回调” 是干啥的
- 作用:专门用来处理 “异步操作完成后,需要执行的逻辑”(比如状态更新后打印、调用接口等)。
- React 里的用法:
- 类组件:
setState(更新内容, 回调函数)
。 - 函数组件:用
useEffect
监听状态,替代回调。
- 类组件:
简单说就是 “等异步操作(比如状态更新)完成后,再执行的一段代码” 。以后再碰到 “状态更新后想干点啥” 的需求,就用回调或者 useEffect
,保证你能拿到最新的状态~
7 回调函数格式是什么,就直接()也可?
回调函数的格式其实很灵活,核心是 “定义一个函数,让它在特定时机(比如状态更新后)被调用” 。下面分场景给你讲清楚怎么写,包你看完就会用 👇
1. 类组件里 setState
的回调格式
在类组件中,setState
的第二个参数就是回调函数,格式可以是:
(1)匿名函数(最常用,直接写逻辑)
jsx
this.setState(
{ persons: [...this.state.persons, "me"] },
// 匿名函数:直接把逻辑写在 () => {} 里
() => {
console.log("状态更新后的数据:", this.state.persons);
}
);
(2)单独定义函数(逻辑复杂时,抽成函数更清晰)
jsx
// 先定义一个回调函数(可以是箭头函数或普通函数)
const afterUpdate = () => {
console.log("状态更新后的数据:", this.state.persons);
};
// 然后传给 setState 当第二个参数
this.setState(
{ persons: [...this.state.persons, "me"] },
afterUpdate // 注意:这里不加 (),加了会立刻执行!
);
⚠️ 注意:传函数的时候 不要加括号(比如 afterUpdate()
会立刻执行,而我们需要 React 在状态更新后执行,所以只传函数名)。
2. 函数组件里 useEffect
的 “回调” 格式
在函数组件中,useEffect
本身就是用来处理 “状态更新后执行逻辑” 的,它的第一个参数就是回调函数,格式:
(1)匿名函数(直接写逻辑)
jsx
useEffect(() => {
// 状态更新后执行的逻辑
console.log("persons 变化了,新数据是:", persons);
}, [persons]); // 监听 persons 变化
(2)单独定义函数(逻辑复杂时抽离)
jsx
// 定义回调函数
const handlePersonsUpdate = () => {
console.log("persons 变化了,新数据是:", persons);
};
// 传给 useEffect
useEffect(handlePersonsUpdate, [persons]);
这里和类组件类似:传函数的时候不加括号,React 会在依赖([persons]
)变化时,自动调用这个函数。
3. 总结:回调函数的 “长相”
不管是类组件的 setState
回调,还是函数组件的 useEffect
回调,格式就两种:
场景 | 格式示例(匿名函数) | 格式示例(单独定义函数) |
---|---|---|
类组件 setState | () => { /* 逻辑 */ } | const fn = () => { /* 逻辑 */ }; 然后传 fn |
函数组件 useEffect | () => { /* 逻辑 */ } | const fn = () => { /* 逻辑 */ }; 然后传 fn |
核心记住:
- 回调函数是 “一段等待被调用的逻辑” ,可以是匿名的(直接写逻辑),也可以是单独定义的函数(抽离复用)。
- 传回调的时候 不要加括号(加了会立刻执行,达不到 “等时机” 的效果)。
以后写回调,照着这两种格式套就行,多写几次就熟练啦~
===================================
一头雾水。。。。。。。需要好好消化。。
加油!