更改组件中的反应道具作为优化



>问题

*这不完全是我的情况,但这是重要的。
假设一个应用呈现元素列表:

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 中更新

阿拉伯数字。直接更新元素的道具,forceUpdate()

  • 将元素重写为类而不是功能组件
  • at 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. 你看到还有什么其他方法可以提高性能吗?我很乐意尝试更不像反应的建议,可能会考虑部分或全部放弃反应。

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

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

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

你可以这样做

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}/> ... }

现在在元素列表中

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>
)
}
} 

最新更新