React.PureComponent 和子组件

6 javascript rerender reactjs react-props react-component

有人请用一个简单的例子向我解释 React 文档的最后一段,它是关于 React.PureComponent 的,我见过的所有示例都是高级的,我刚刚开始了解这个概念,但我无法确切地看到它是什么指。正是这一点,孩子也应该“纯洁”。据我所知,我认为这种说法有些言过其实,因为如果父亲不重新呈现自己,那么孩子们也不会重新呈现自己。,或者,是否有什么东西我无法逃脱而我无法想象?这就是为什么我需要一个简单的例子,我已经在这里查看了所有类似的问题,但它们是高级示例,并不涵盖我正在寻找的内容。

此外,React.PureComponent 的 shouldComponentUpdate() 会跳过整个组件子树的 prop 更新。确保所有子组件也是“纯”的

Amb*_*ier 0

我做了一些东西可以玩。

\n

我认为要认识到的重要部分是,只有一个区别,那shouldComponentUpdate就是PureComponent.

\n

我也很困惑Make sure all the children components are also \xe2\x80\x9cpure\xe2\x80\x9d。根据我的理解,这只是一个警告、一个指导方针,而不是绝对不要这样做。当您将父级设置为 PureComponent 时,所有子级都会受到影响。但是不习惯shouldComponentUpdate(PureComponent)的人可能会对孩子在树中混合时的行为(不更新)感到惊讶。

\n

以第二个片段为例this.state.quotes[0].text = "StackOverflow save the day";。当Component同时使用<List/>和 时<ListElement/>,任何一个setState都会更新视图。但是当PureComponent仅使用 on时<List/>,有人可能期望<ListElement/>更新,但它不会。

\n

想象一下,他们不会写出<List/>纯粹和<ListElement/>不纯粹的内容,而是警告不要做的事情。你会把两者都写成纯粹的。你会考虑做类似的事情吗this.state.quotes[0].text = "StackOverflow save the day";?不,你会从 开始this.state.quotes[0] = {...}。然后你会看看哪个组件处理上面的数据,quotes数组,处理数组的组件,是纯粹的吗?是的,那就写this.state = [{...}]吧。您执行此“重新创建引用/深层复制”操作,直到找到第一个非纯组件。如果您遵循他们的建议,在第一个非纯组件处,您知道不会有任何其他过时的引用阻止您的渲染。

\n

如果使用深度比较,你永远不会遇到这个问题。我正在使用钩子组件,它们更加友好。我建议您跳过类组件并使用钩子。shouldComponentUpdate在钩子中使用React.memo来实现。由于您决定如何比较 props,因此很明显,如果您比较previousList === newList(浅比较),您将错过列表中的任何更新/添加/删除。当然,你会进行深度比较(使用deep-equallib 或 (avoid) )JSON.stringify(previousList) === JSON.stringify(newList)。请注意,这样React.Memo做更好还有另一个原因,它只处理 props 比较,而不是像 PureComponent 那样同时处理状态和 props 比较。

\n

我不认为你需要每个孩子都使用浅层比较,如果组件渲染得足够快,使用shouldComponentUpdate(或React.memo) 会使情况变得更糟(记住这是一种性能优化)。

\n

下面演示了我们可以通过浅层 props 比较(第一个片段)和浅层状态比较(第二个片段)得到的问题:

\n
import { Component, PureComponent } from \'react\';\n\ninterface Quote {\n  author: string;\n  text: string;\n}\n\nexport class Playground extends Component {\n  state = {\n    quotes: [\n      {\n        author: \'Ambroise\',\n        text: \'I like React.\'\n      }\n    ]\n  }\n\n  onClickOK () {\n    this.setState({\n      quotes: [\n        {\n          author: \'Ariel\',\n          text: \'Prefer VueJS.\'\n        }\n      ]\n    });\n  }\n\n  // https://reactjs.org/docs/react-api.html#reactpurecomponent\n  // React.PureComponent implements it with a shallow prop and state comparison\n  // "prop comparison"\n  onClickIssue() {\n    if (window[\'dontMix\']) {\n      alert("Please don\'t mix on click issues. Reload page.")\n      return;\n    }\n    window[\'dontMix\'] = true;\n\n    // This won\'t work if <List/> is a PureComponent\n    // Note that we ALSO CHANGED the props for <ListElement/> here, different object, differents strings,\n    // but <ListElement/> will NOT re-render, regardless of <ListElement/> being pure or not.\n    const quotesUpdated = this.state.quotes;\n    quotesUpdated.pop();\n    quotesUpdated.push({\n      author: \'Thomas\',\n      text: \'Prefer Angular.\'\n    });\n\n    // Shallow props comparison, <List/> will compare array references\n    // `quotesUpdated === this.state.quotes` and because it is true,\n    // it will not re-render, or any of the childrens.\n    //\n    // The quote object is different, so <ListElement/>\n    // would be expected to re-render.\n    this.setState({\n      quotes: quotesUpdated\n    });\n  }\n\n  onClickIssue2() {\n    if (window[\'dontMix\']) {\n      alert("Please don\'t mix on click issues. Reload page.")\n      return;\n    }\n    window[\'dontMix\'] = true;\n\n    // This won\'t work if <ListElement/> is a PureComponent since the object in first index stay the same.\n    // Since we recreate the array, <List/> will always re-render regardless of <List/> being pure.\n    this.state.quotes[0].author = "Thomas";\n    this.state.quotes[0].text = "Prefer Angular.";\n    this.setState({\n      quotes: [\n        this.state.quotes[0],\n      ]\n    });\n  }\n\n  render() {\n    return (\n      <section>\n        <button onClick={this.onClickOK.bind(this)}>Get updated</button>\n        <button onClick={this.onClickIssue.bind(this)}>Problem</button>\n        <button onClick={this.onClickIssue2.bind(this)}>Problem2</button>\n        <List quotes={this.state.quotes}/>\n      </section>\n    );\n  }\n}\n\n// Change PureComponent to Component, no problem with shallow comparison anymore.\nclass List extends PureComponent<{ quotes: Quote[]; }>{\n  render() {\n    return (\n      <ul>\n        {\n          this.props.quotes.map(\n            (e, i) => <ListElement key={i} quote={e}/>\n          )\n        }\n      </ul>\n    );\n  }\n}\n\nclass ListElement extends PureComponent<{quote: Quote}> {\n  render() {\n    return (\n      <li>\n        <h3>{this.props.quote.author}</h3>\n        <p>{this.props.quote.text}</p>\n      </li>\n    );\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

第二个片段,重点关注状态浅层比较,并且这次也<ListElement/>以 a 而不是 a 开头。ComponentPureComponent

\n
import { Component, PureComponent } from \'react\';\n\ninterface Quote {\n  author: string;\n  text: string;\n}\n\nexport class Playground2 extends Component {\n  state = {\n    quotes: [\n      {\n        author: \'Ambroise\',\n        text: \'I like React.\'\n      }\n    ]\n  };\n\n  render() {\n    return (\n      <section>\n        <List quotes={this.state.quotes}/>\n      </section>\n    );\n  }\n}\n\n// Note: Careful not to confuse state.quotes and props.quote\nclass List extends PureComponent<{ quotes: Quote[]; }> {\n  state = {\n    quotes: this.props.quotes\n  };\n\n  // https://reactjs.org/docs/react-api.html#reactpurecomponent\n  // React.PureComponent implements it with a shallow prop and state comparison\n  // "state comparison"\n  fail() {\n    for (let i = 0; i < this.state.quotes.length; i++) {\n      this.state.quotes.pop();\n    }\n\n    this.setState({\n      quotes: this.state.quotes\n    });\n  }\n\n  success() {\n    // This will never work, because `previousState === newState` (both are this.state)\n    // this.state.quotes = [];\n    // this.setState(this.state);\n\n    this.setState({\n      quotes: []\n    });\n  }\n\n  // It work if you change this class to Component\n  changeChild() {\n    // if you clicked another button reload the page to get initial state.\n    if (this.state.quotes.length === 0) {\n      alert("You clicked another button. Please reload page to test this one.");\n    }\n\n    // previously "I like React."\n    this.state.quotes[0].text = "StackOverflow save the day";\n\n    // NOTICE: Choose only one setState bellow, comment the others.\n\n    // Won\'t work\n    // this.setState({\n    //   quotes: this.state.quotes\n    // });\n\n    // This will never work, because `previousState === newState` (both are this.state)\n    // this.state.quotes = [this.state.quotes[0]];\n    // this.setState(this.state);\n\n    // Won\'t work\n    this.setState(this.state);\n\n    // This will work when <List/> is a PureComponent and <ListElement/> a Component,\n    // both this.state and this.state.quotes are different\n    // this.setState({\n    //   quotes: [this.state.quotes[0]]\n    // });\n  }\n\n  render() {\n    return (\n      <div>\n        <button onClick={this.fail.bind(this)}>Empty the list (FAIL if PureComponent)</button>\n        <button onClick={this.success.bind(this)}>Empty the list (SUCCESS)</button>\n        <button onClick={this.changeChild.bind(this)}>Change child (FAIL if PureComponent)</button>\n        <ul>\n          {\n            this.state.quotes.map(\n              (e, i) => <ListElement key={i} quote={e}/>\n            )\n          }\n        </ul>\n      </div>\n    );\n  }\n}\n\n// ATTENTION: This one start as a Component this time, instead of a PureComponent\nclass ListElement extends Component<{quote: Quote}, {author: string}> {\n  state = {\n    author: this.props.quote.author,\n  };\n\n  // Yep this work.\n  takeOwnership() {\n    this.setState({\n      author: "Constantin"\n    })\n  }\n\n  render() {\n    return (\n      <li>\n        <button onClick={this.takeOwnership.bind(this)}>Take ownership !</button>\n        <h3>{this.state.author}</h3>\n        <p>{this.props.quote.text}</p>\n      </li>\n    );\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

太长了;我也发现它令人困惑,它看起来更像是建议,我建议不要过多关注它,并使用钩子组件,在其中您可以对道具/状态比较和浅/深比较进行更细粒度的控制。

\n