更改组件内的响应道具以进行优化

时间:2018-10-05 18:25:16

标签: javascript reactjs react-props react-lifecycle react-state-management

问题

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

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)}
  • 在ElementsList中: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的道具和forceUpdate()

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

输入更改时发生的事件:

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

问题

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

  1. 通过第二种方法,我会获得更好的性能吗? (请记住,有很多元素)
  2. 这种方法可能导致哪些具体问题?是的,是的,我知道代码的透明度不太高,这不是它的本意。
  3. 您是否还有其他提高性能的方法?我很乐意尝试更少的类似反应的建议,可以考虑部分或全部放弃反应。

2 个答案:

答案 0 :(得分:1)

不变的数据结构通过备忘录和结构共享之类的技术解决了您的性能问题。

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

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

答案 1 :(得分: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>
        )
        }
       }