6 javascript rerender reactjs react-props react-component
有人请用一个简单的例子向我解释 React 文档的最后一段,它是关于 React.PureComponent 的,我见过的所有示例都是高级的,我刚刚开始了解这个概念,但我无法确切地看到它是什么指。正是这一点,孩子也应该“纯洁”。据我所知,我认为这种说法有些言过其实,因为如果父亲不重新呈现自己,那么孩子们也不会重新呈现自己。,或者,是否有什么东西我无法逃脱而我无法想象?这就是为什么我需要一个简单的例子,我已经在这里查看了所有类似的问题,但它们是高级示例,并不涵盖我正在寻找的内容。
“此外,React.PureComponent 的 shouldComponentUpdate() 会跳过整个组件子树的 prop 更新。确保所有子组件也是“纯”的”
我做了一些东西可以玩。
\n我认为要认识到的重要部分是,只有一个区别,那shouldComponentUpdate就是PureComponent.
我也很困惑Make sure all the children components are also \xe2\x80\x9cpure\xe2\x80\x9d。根据我的理解,这只是一个警告、一个指导方针,而不是绝对不要这样做。当您将父级设置为 PureComponent 时,所有子级都会受到影响。但是不习惯shouldComponentUpdate(PureComponent)的人可能会对孩子在树中混合时的行为(不更新)感到惊讶。
以第二个片段为例this.state.quotes[0].text = "StackOverflow save the day";。当Component同时使用<List/>和 时<ListElement/>,任何一个setState都会更新视图。但是当PureComponent仅使用 on时<List/>,有人可能期望<ListElement/>更新,但它不会。
想象一下,他们不会写出<List/>纯粹和<ListElement/>不纯粹的内容,而是警告不要做的事情。你会把两者都写成纯粹的。你会考虑做类似的事情吗this.state.quotes[0].text = "StackOverflow save the day";?不,你会从 开始this.state.quotes[0] = {...}。然后你会看看哪个组件处理上面的数据,quotes数组,处理数组的组件,是纯粹的吗?是的,那就写this.state = [{...}]吧。您执行此“重新创建引用/深层复制”操作,直到找到第一个非纯组件。如果您遵循他们的建议,在第一个非纯组件处,您知道不会有任何其他过时的引用阻止您的渲染。
如果使用深度比较,你永远不会遇到这个问题。我正在使用钩子组件,它们更加友好。我建议您跳过类组件并使用钩子。shouldComponentUpdate在钩子中使用React.memo来实现。由于您决定如何比较 props,因此很明显,如果您比较previousList === newList(浅比较),您将错过列表中的任何更新/添加/删除。当然,你会进行深度比较(使用deep-equallib 或 (avoid) )JSON.stringify(previousList) === JSON.stringify(newList)。请注意,这样React.Memo做更好还有另一个原因,它只处理 props 比较,而不是像 PureComponent 那样同时处理状态和 props 比较。
我不认为你需要每个孩子都使用浅层比较,如果组件渲染得足够快,使用shouldComponentUpdate(或React.memo) 会使情况变得更糟(记住这是一种性能优化)。
下面演示了我们可以通过浅层 props 比较(第一个片段)和浅层状态比较(第二个片段)得到的问题:
\nimport { 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}\nRun Code Online (Sandbox Code Playgroud)\n第二个片段,重点关注状态浅层比较,并且这次也<ListElement/>以 a 而不是 a 开头。ComponentPureComponent
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}\nRun Code Online (Sandbox Code Playgroud)\n太长了;我也发现它令人困惑,它看起来更像是建议,我建议不要过多关注它,并使用钩子组件,在其中您可以对道具/状态比较和浅/深比较进行更细粒度的控制。
\n