重新渲染React后代组件,而无需重新渲染祖先

时间:2019-08-21 06:21:56

标签: reactjs

我正在尝试使用React 16.8.3构建一个Navbar。我想使用组合来传递Navbar内容,而不是通过prop来传递配置对象,以便具有更大的灵活性。像这样:

<Navbar>
    <NavItem>Some label</NavItem>
    <NavItem>
        <span>Some arbitrary content</span>
    <NavItem>
</Navbar>

代替:

const navItems = [
  {
    label: 'Some label'
  },
  {
    label: 'Some other label'
  }
]

<Navbar items={navItems} />

到目前为止,导航栏工作正常。我在shouldComponentUpdate方法中添加了一些逻辑,以防止多次重新渲染:

shouldComponentUpdate(nextProps) {
   return nextProps.selectedItem !== this.props.selectedItem;
}

因此,Navbar仅在其选定项更改时才重新渲染,而不是在Navbar父项重新渲染时不重新渲染。

问题是,一个NavItem包含一个带有任务计数的徽章,每当用户执行某些任务时,该徽章必须更新:

Todos screenshot

,项目标记为:

<Navbar>
    <NavItem>
        <div className="has-badge">
           <span>Label</span>
           <span className="badge">{this.props.toDoCount}</span>
        </div>
    </NavItem>
</Navbar>

this.props.toDoCount 是Navbar父项的道具,而不是Navbar本身的道具。

如何在不重新呈现整个导航栏的情况下更新徽章编号 ?到目前为止,我已经尝试创建一个Badge组件,添加一些状态以及使用Navbar父级中的ref更新徽章编号的方法:

import React, { PureComponent } from 'react';

interface BadgeProps {
  number: number;
}

class Badge extends PureComponent<BadgeProps> {
  state = {
    number: 0
  };

  setCount(number) {
    this.setState({
      number
    });
  }

  render() {
    return <span className="badge">{this.state.number}</span>;
  }
}

在导航栏父项中:

private todos = createRef<Badge>();

...

componentDidUpdate(prevProps: EhrProps) {    
   this.todos.current.setCount(toDosCount);
}

它正在工作,但是...在React中有没有更简单或更干净的方法?

谢谢!

PS:我们在项目中使用Redux,但我想避免在Navbar或其项目中使用商店。

编辑:

我在Navbar的render方法中使用React.children和React.cloneElement:

render() {
    const { className, children, selectedItem, ...rest } = this.props;
    const classes = classNames(
      {
        navbar: true
      },
      className
    );

    return (
      <nav className={classes} {...rest}>
        {React.Children.map(children, child => {
          if (child.type === NavItem) {
            return React.cloneElement(child, {
              onClick: this.handleItemClick,
              selected: child.props.name === selectedItem
            });
          }
          return child;
        })}
      </nav>
    );
  }

每个NavItem都会处理自己的渲染:

return (
    <div className={classes} onClick={handleClick} onKeyPress={handleKeyPress} role="menuitem" tabIndex={0}>
      {children}
    </div>
);

1 个答案:

答案 0 :(得分:0)

大概是Navbar组件的一些代码看起来像这样。

class Navbar extends React.Component<Props> {
  render() {
    return (
      <div>
        {this.props.navItem.map(item => <NavItem key={item.label}>{item.label}</NavItem>)};
      </div>
    );
  }
}

,然后提供一些代码来渲染每个孩子NavItem

要使组件相当有效,只需重新渲染整个Navbar就足够了,而不必重新渲染每个孩子。

我推荐的是:

  1. 使Navbar的每个子代都在其自己的组件中呈现;在上方的组件中称为NavItem
  2. 使用componentShouldUpdateReact.PureComponent(研究一下!一旦理解,这是一个很好的通用解决方案,默认情况下对每个组件使用React.Component代替),以确保每个子组件仅在其值更改时重新渲染

当您更新单个NavItem的标志时,将发生Navbar重新渲染的情况。大多数NavItems会看到其道具没有更改,也没有重新呈现。拥有徽章的Navbar的单个孩子将被更改,并将重新呈现。这样,实际的开销实际上很低。

如果您的Navbar拥有大量孩子,或者该单身孩子的徽章发生了很大变化,则您可以使用React.Context或Redux传递该单身孩子的价值来进一步优化,但感觉很混乱,似乎过早了优化。

祝你好运!