1

问题

*这不完全是我的情况,但这很重要。
想象一个呈现元素列表的应用程序:

const ElementsList = ({elements, <onElementTextChange>}) => (
  <div className='ElementsList'>
    {elements.map(el => (
      <Element
        key={el.id}
        element={el}
      />
    ))}
  </div>
)

元素保持在渲染的父组件的状态ElementsList。每个元素都是一个简单的对象:

this.state = {elements: [
  {id: 1, text: 'apple'},
  {id: 23, text: 'banana'},
]}
...
render() { ... <ElementsList elements={this.state.elements}/> ... }

每个元素与附近的输入字段一起呈现以更改其文本:

export const Element = ({element, <onTextChange>}) => (
  <div className='Element'>
    <div className='element-text'>{element.text}</div>
    <input type='text' value={element.text} onChange={???}></input>
  </div>
)

考虑到可能有很多元素(应用程序可以处理的越多越好)。
现在的问题是我们应该如何通过这些输入来更新文本。

解决方案

我看到 2 个选项(它们的一些组合是可能的):

1.正确的反应方式(是吗?) - 通知父组件有关更改

  • 在元素:onChange={evt => onTextChange(evt.target.value)}
  • 在元素列表:onTextChange={newText => onElementTextChange(el, newText)}
  • 在父组件:onElementTextChange={ (element, newText) => {make new elements array with the updated element somehow; setState(elements)} }

该解决方案似乎难以实施。与下面的替代方案相比,它也很慢。输入更改时发生的事件(如果我错了,请纠正我):

  1. 通过组件链通知父组件有关更改
  2. 父组件构造新元素数组并调用setState
  3. 整个虚拟 DOM 得到更新(包括每个元素)
  4. 更新的虚拟 DOM 与之前的虚拟 DOM 版本进行比较
  5. 一个元素在真实 DOM 中更新

2.直接更新Element的propsforceUpdate()

  • 将 Element 重写为类而不是功能组件
  • 在元素处:onChange={evt => {element.text = evt.target.value; this.forceUpdate()}}

输入更改时发生的事件:

  1. props.element.text 被改变并被forceUpdate调用
  2. 虚拟 DOM 中只有一个元素被更新
  3. 将虚拟 DOM(一个元素)的更新子树与旧虚拟 DOM 进行比较
  4. 一个元素在真实 DOM 中更新

问题

目前我正在使用一种中间方法,但更多的是在“适当的反应方式”方面。而且速度还不够快。
我更喜欢第二种选择,它需要更少的代码,而且看起来它应该工作得更快(理论上)。

  1. 使用第二种方法会显着提高性能吗?(记住有成千上万的元素)
  2. 这种方法会导致哪些具体问题?是的,是的,我知道代码不那么透明,这不是它的本意。
  3. 您还有其他提高性能的方法吗?我很乐意尝试更少的类似反应的建议,可能会考虑部分或完全放弃反应。
4

2 回答 2

1

不可变数据结构通过记忆和结构共享等技术解决您的性能问题。

您应该考虑使用Immutable.js。然后,您可以使用React.PureComponent对大型数据结构进行有效的值相等检查。

一个缺点是 Immutable.js 数据结构不能与常规的可变 javascript 数据结构很好地混合,这可能会令人沮丧。Elm 或 ClojureScript 等一些语言为这些类型的数据结构提供一流的支持,使其体验更加一致。

于 2018-10-05T19:03:02.053 回答
1

你可以这样做

 this.state = {elements: [
  {id: 1, text: 'apple'},
   {id: 23, text: 'banana'},
   ]}

   // creating a update function which you will pass down to the child

    updateState = (id, val) => {
           const newElements = this.state.elements;
            newElements.map(item => {
               if(item.id === id) item.text = val;
             });
          this.setState({
           elements: newElements,
          })
       }
 render() { ... <ElementsList elements={this.state.elements} updateState ={this.updateState}/> ... }

现在在 ElementsList

       const ElementsList = ({elements, updateState,  <onElementTextChange>}) => (
       <div className='ElementsList'>
        {elements.map(el => (
       <Element
        key={el.id}
        element={el}
        updateState = {updateState}
         />
        ))}
        </div>
         )

现在在元素中

    export class Element extends React.Component{

     state = {
       value: '',  
       }
         onChangeValue = (e) => { 
              this.setState({ value: e.target.value});
            // calling the parent function directly
               this.props.updateState(this.props.element.id, e.target.value);
            }
       componentDidMount(){
         this.setState({
         value: this.props.element.text,
       })
         }
      render(){
        return(
              <div className='Element'>
       <div className='element-text'>{element.text}</div>
       <input type='text' value={this.state.value} onChange={this.onChangeValue}></input>
       </div>
        )
        }
       } 
于 2018-10-05T20:01:57.870 回答