React受控组件如何优雅接收父组件更新

"未经审视的生活不值得过"------ 苏格拉底

有这样一个开发场景:

封装一个自定义组件,接收父级的props传进来做value,同时自己也是受控组件

给几分钟时间思考下大概怎么写。

起手就写

看到大部分人都是这样写

jsx 复制代码
const Com = ({
    value
}) => {
    const [selfValue, setSelfValue] = useState(value);
    useEffect(() => {
        setSelfValue(value);
    }, [value]);
    return <div>
                <input 
                    value={selfValue}
                    onChange = {e => {setSelfValue(e.target.value);}}
                />
            </div>
}

在这个例子中,每次 value props 变化时,useEffect 都会触发,导致 selfValue 被更新。虽然功能上可以实现,但这种方式会增加不必要的渲染开销,尤其是在复杂组件中,频繁的 useEffect 触发可能会导致性能问题。

相信各位Reacter都会使用useEffect来处理这种情况【对标vue的watch?】,即通过"监听"父组件传进来的值来更新到子组件内部的值

然而react是没有监听这个概念的,它,叫同步副作用。

关于useEffect滥用

关于这个讨论,网上的文章其实也有很多,大家可以自行查阅。其实滥用useEffect除了造成排查问题更加困难,还有一个更加直观的问题,就是会多一次渲染。

毕竟useEffect是需要render后才会执行回调函数,当父组件更新了value,子组件先render一次,useEffect再set一次,render第二次。

自定义hook

于是乎问了下各个ai,发现全部都是使用useEffect,呵呵,还是不够智能。还是自己亲手下场。

设计思路

  1. 先设想希望用户怎么用,大概的模样

    const [count, setCount] = useStateFromProps(props.count)

  2. props.count跟count同步,不使用useEffect来同步,那就直接return props.count?当然不行,因为count还得受控,那就使用useRef【curValue】做中间商,注意:更改时强行update一次,不然视图不会更新

  3. setValue得模仿useState返回的的第二个参数,即支持入参是函数,同时更新一下useRef的值,这样子组件就可以接收父组件做初始值,同时也自己能受控了

  4. 父组件更新时,子组件的值也得更新,那就弄多一个useRef【oldValue】存储旧值,每次render将【oldValue】和【curValue】做比较,不同的话证明props.count变了,【curValue】得变成props.count

tsx 复制代码
import { useState, useRef, useMemo } from 'react';

const useStateFromProps = <T>(props: T) => {
  const [, forceUpdate] = useState({});
  const oldValue = useRef<T>(props);
  const curValue = useRef<T>(props);
  if (oldValue.current !== props) {
    oldValue.current = props;
    curValue.current = props;
  }
  return useMemo(
    () =>
      [
        curValue.current,
        (prevState: T) => {
          const updatedValue =
            typeof prevState === 'function' ? prevState(curValue.current) : prevState;
          curValue.current = updatedValue;
          forceUpdate({});
        },
      ] as [T, React.Dispatch<React.SetStateAction<T>>],
    [props, curValue.current],
  );
};

export default useStateFromProps;

这段代码的核心逻辑是:它通过 useRef 来存储旧的 props 和当前的值,当 props 发生变化时,更新 curValue。同时,返回一个类似 useState 的数组,第一个元素是当前状态值,第二个元素是用于更新状态的函数。

使用

tsx 复制代码
import { useState } from 'react';
import { useStateFromProps } from '@wjjhhh/jhooks';

const Child = ({ num: parentNum }: { num: number }) => {
  const [num, setNum] = useStateFromProps(parentNum);
  console.count('child render');
  return (
    <div>
      <div>
        child state: {num}
        <button onClick={() => setNum(num + 1)}>add</button>
      </div>
    </div>
  );
};

export default () => {
  const [num, setNum] = useState(0);
  console.count('parent render');
  return (
    <div>
      <div>
        parent state: {num} <button onClick={() => setNum(num + 1)}>add</button>
      </div>
      <Child num={num} />
    </div>
  );
};

结语

第一次掘金发文,很多格式和工具都不会用,多多包涵。同时请大家支持小弟封装的react hooks库,专做一些稀奇古怪功能的hooks库

求点击进入点⭐

相关推荐
牧羊狼的狼21 分钟前
React 中的 HOC 和 Hooks
前端·javascript·react.js·hooks·高阶组件·hoc
知识分享小能手2 小时前
React学习教程,从入门到精通, React 属性(Props)语法知识点与案例详解(14)
前端·javascript·vue.js·学习·react.js·vue·react
魔云连洲2 小时前
深入解析:Vue与React的异步批处理更新机制
前端·vue.js·react.js
mCell2 小时前
JavaScript 的多线程能力:Worker
前端·javascript·浏览器
超级无敌攻城狮4 小时前
3 分钟学会!波浪文字动画超详细教程,从 0 到 1 实现「思考中 / 加载中」高级效果
前端
excel5 小时前
用 TensorFlow.js Node 实现猫图像识别(教学版逐步分解)
前端
gnip5 小时前
JavaScript事件流
前端·javascript
赵得C5 小时前
【前端技巧】Element Table 列标题如何优雅添加 Tooltip 提示?
前端·elementui·vue·table组件
wow_DG5 小时前
【Vue2 ✨】Vue2 入门之旅 · 进阶篇(一):响应式原理
前端·javascript·vue.js
weixin_456904275 小时前
UserManagement.vue和Profile.vue详细解释
前端·javascript·vue.js