Web 开发 13

React组件传值及回调函数知识详解

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 讲的核心就是这个意思:

  1. 数据应该集中存放在共同的父组件中(作为 state)
  2. 子组件需要使用这些数据时,通过 props 从父组件获取
  3. 兄弟组件之间不能直接传值,必须通过它们的共同父组件作为 "中转站"

这种模式在 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. 总结 “传值判断逻辑”(以后碰到类似问题套用):

  1. 明确子组件功能:先想子组件要干啥,需要哪些数据支撑(比如这里 <Profile/> 要控制按钮状态)。
  2. 匹配父组件数据:看父组件(这里是 <Suggestions/> )有的数据里,哪些能满足子组件功能需求。
  3. 判断是否传递:子组件需要的,就传;用不上的,别传(传了也是多余,还可能让数据 flow 混乱)。

简单说就是 “子组件功能决定传什么” 。比如做 “购物车组件”,子组件要显示商品总价,那父组件的 “商品列表 + 价格” 数据就得传;要是子组件只负责 “展示商品图片”,可能就不用传价格数据 。多结合实际功能需求想,慢慢就懂啦,你已经在靠近 React 传值的 “灵魂” 啦。

setState 是异步操作

1. 先看代码想干啥

有个状态 persons,初始是空数组 [] 。
testingStuff 函数里,先用 setPersons 给 persons 加个元素 'me',然后 console.log(persons) 想看看更新后的数据 。

2. 关键:setState(这里是 setPersons )是“异步”的

  • “异步” 意思:调用 setPersons 后,React 不会 “立刻” 把 persons 改成新值,而是先记下来 “要更新”,等合适时机(比如当前函数执行完、批量更新优化)才真正修改状态 。
  • 所以代码执行顺序是这样的
    1. 调用 setPersons,告诉 React “我要把 persons 改成 [...persons, 'me']”(但还没真正改)。
    2. 接着执行 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 还没更新'); 
};
  • 执行顺序:
    1. 调用 setPersons,触发状态更新(异步,先记下来要更新)。
    2. 先执行 setPersons 后面的代码(比如上面的 console.log )。
    3. 等状态真正更新完成后,才执行回调函数里的代码(打印更新后的 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

核心记住:

  • 回调函数是 “一段等待被调用的逻辑” ,可以是匿名的(直接写逻辑),也可以是单独定义的函数(抽离复用)。
  • 传回调的时候 不要加括号(加了会立刻执行,达不到 “等时机” 的效果)。

以后写回调,照着这两种格式套就行,多写几次就熟练啦~

===================================

一头雾水。。。。。。。需要好好消化。。

加油!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值