react 父子组件的渲染机制 | 优化手段

父子组件的渲染机制

渲染分初次渲染重新渲染

React组件会在两种情况下发生重新渲染

  • 当组件自身的state发生变化
  • 当组件的父组件重新渲染
    当一个父组件被触发渲染时,其所有子组件都会重新渲染(子组件的子组件也会)

但有些场景下我们并不希望所有的子组件都重新渲染,比如在一个列表中,我们只希望重新渲染单击受新选择影响的这项。

优化手段与实践写法

文章1::https://juejin.cn/post/7251861916146417723

父组件:下发state

在一个组件中,一部分组件使用了 state ,而另一部分组件和 state 相对孤立,此时可以将使用的了state的组件拆分为子组件。

优化前

// 优化前写法
const Component = () => {
  const [isOpen, setOpen] = useState(false)
  return (
    <div>
        <button onClick={() => setOpen(!isOpen)}>open</button>
        { isOpen && <ModalDialog />}
        {/* 状态的变化会引起 SlowComponent 重复渲染 */}
        <SlowComponent />
    </div>
  )
}

优化后
优化思路:将使用了state的组件拆分为一个子组件,state在子组件中使用(将state下发到子组件),state变化时仅当前组件重渲染。

// 优化后写法
const Component = () => {
  return (
    <div>
        <ButtonWithDialog />
        <SlowComponent />
    </div>
  )
}
const ButtonWithDialog = () => {
  const [isOpen, setOpen] = useState(false)
  return (
    <>
        <button onClick={() => setOpen(!isOpen)}>open</button>
        { isOpen && <ModalDialog />}
    </>
  )
}

props.children 传递无状态组件

这两个方法其实思路都是一样的,就是拆分受state影响的组件与不受state影响的组件。

有时无法轻易的把一个组件单独的独立提取出来,此时可以把带状态的组件提取出来,然后把耗时的组件作为 props .children 传递。

优化前

const FullComponent = () => {
  const [state, setState] = useState(1);

  const onClick = () => {
    setState(state + 1);
  };

  return (
    <div onClick={onClick} className="click-block">
      <p>Click this component - "slow" component will re-render</p>
      <p>Re-render count: {state}</p>
      <VerySlowComponent />
    </div>
  );
};

优化后
优化思路:父组件传递props对于引用类型来说其实传递是地址,也就是在子组件中使用props引用类型其实是使用的地址值。执行父组件的render的时候,比较发现props.children的引用地址没变化。

本方法与组件形式引用的区别:组件重新渲染其实是执行render方法, 如果子组件采用组件形式引入(可以理解为这里引入的是子组件render方法的执行结果。)
每次父组件重新渲染都会执行子组件的render方法获取新的执行结果。

const SplitComponent = () => {
  return (
    <>
      <ComponentWithClick>
        <>
          <p>Click the block - "slow" component will NOT re-render</p>
          <VerySlowComponent />
        </>
      </ComponentWithClick>
    </>
  );
};
const ComponentWithClick = ({ children }) => {
  const [state, setState] = useState(1);
  const onClick = () => {
    setState(state + 1);
  };
  return (
    <div onClick={onClick} className="click-block">
      <p>Re-render count: {state}</p>
      {children}
    </div>
  );
};
props传递组件

该方法与props.children本质是一样的,只不过有些时候如果无法通过props.children传递,可以将组件作为props的参数传递。

优化前

const FullComponent = () => {
  const [state, setState] = useState(1);

  const onClick = () => {
    setState(state + 1);
  };

  return (
    <div onClick={onClick} className="click-block">
      <p>Click this component - "slow" component will re-render</p>
      <p>Re-render count: {state}</p>
      <VerySlowComponent />
      <p>Something</p>
      <AnotherSlowComponent />
    </div>
  );
};

优化后
优化思路:props 不受状态变化的影响,所以可以避免耗时组件的重复渲染。适用于耗时组件不受状态变化的影响,又不能作为 children 属性传递

const ComponentWithClick = ({ left, right }) => {
  const [state, setState] = useState(1);

  const onClick = () => {
    setState(state + 1);
  };

  return (
    <div onClick={onClick} className="click-block">
      <p>Re-render count: {state}</p>
      {left}
      <p>Something</p>
      {right}
    </div>
  );
};

// 把组件作为 props 传递给组件,这样耗时组件就不受点击事件的影响
const SplitComponent = () => {
  const left = (
    <>
      <h3>component with slow components passed as props</h3>
      <p>Click the block - "slow" components will NOT re-render</p>
      <VerySlowComponent />
    </>
  );
  const right = <AnotherSlowComponent />;
  return (
    <>
      <ComponentWithClick left={left} right={right} />
    </>
  );
};

React.memo缓存子组件与useCallback结合

React.memo方法是一个高阶函数,参数是一个组件A,返回包装过的新组件B。
包装过的新组件B具有缓存功能,只有组件A的props发生变化,才会触发组件重新渲染。

注意点
这里props 是浅比较,在将对象方法作为 props 传递时必须考虑到引用地址的问题(如果地址变化,也会被认为props变化了)。

解决办法
在父组件中,对于需要传递给子组件的引用类型

  • 使用useCallback缓存函数
  • 使用useMemo缓存函数返回的结果(本场景的作用是缓存对象)

比如选中的子组件高亮,父组件维护一个选中子组件的activeId
优化前写法:在子组件中对比当前Id是否与activeId一致。
点击子组件时,activeId一直变化,所以每个子组件的props会变化。

const children=({activeId,id})=>{
	const isActive = activeId===id;
	return (
		<div className={isActive?'active':''}></div>
	)
}

优化后写法
思路:缓存子组件,当props变化时才渲染。在父组件判断当前子组件是否选中的,如果选中传递className(这里可以自定义props,传递什么都行)。这样的好处是className变化的子组件才会重新渲染。

// 在父组件中使用子组件
<Folder
     className={activeId === item.id ? 'active' : ''}
     key={item.id}
      id={item.id}
 />
  • 18
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
React中,父组件可以通过props传递数据给子组件,子组件可以通过调用父组件传递的函数来向父组件传递数据。 父组件传递数据给子组件: ``` class ParentComponent extends React.Component { constructor(props) { super(props); this.state = { data: "Hello, World!" }; } render() { return ( <div> <ChildComponent data={this.state.data} /> </div> ); } } class ChildComponent extends React.Component { render() { return ( <div> <p>{this.props.data}</p> </div> ); } } ``` 子组件向父组件传递数据: ``` class ParentComponent extends React.Component { constructor(props) { super(props); this.state = { data: "" }; this.handleData = this.handleData.bind(this); } handleData(data) { this.setState({ data: data }); } render() { return ( <div> <ChildComponent onData={this.handleData} /> <p>Received data: {this.state.data}</p> </div> ); } } class ChildComponent extends React.Component { constructor(props) { super(props); this.state = { input: "" }; this.handleChange = this.handleChange.bind(this); this.handleClick = this.handleClick.bind(this); } handleChange(event) { this.setState({ input: event.target.value }); } handleClick() { this.props.onData(this.state.input); } render() { return ( <div> <input type="text" value={this.state.input} onChange={this.handleChange} /> <button onClick={this.handleClick}>Send</button> </div> ); } } ``` 在上述代码中,子组件中的input和button组件用于接收用户输入的数据,并通过调用父组件传递的onData函数向父组件传递数据。父组件中的handleData函数用于接收子组件传递的数据,并将其存储在父组件的state中,然后渲染到页面上。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值