>问题
*这不完全是我的情况,但这是重要的。
假设一个应用呈现元素列表:
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)} }
该解决方案似乎太笨拙而无法实施。与下面的替代方案相比,它也很慢。输入更改时发生的事件(如果我错了,请纠正我(:
- 通过组件链通知父组件有关更改的信息
- 父组件构造新元素数组并调用
setState
- 整个虚拟 DOM 得到更新(包括每个元素(
- 更新后的虚拟 DOM 与以前的虚拟 DOM 版本进行比较
- 一个元素在真实的 DOM 中更新
阿拉伯数字。直接更新元素的道具,forceUpdate()
- 将元素重写为类而不是功能组件
- at Element: onChange={evt => {element.text = evt.target.value; this.forceUpdate((}}
输入更改时发生的事件:
- props.element.text 被更改并调用
forceUpdate
- 虚拟 DOM 中只有一个元素被更新
- 将虚拟 DOM 的更新子树(一个元素(与旧的虚拟 DOM 进行比较
- 一个元素在真实的 DOM 中更新
问题
目前,我使用的是中间方法,但更多的是"适当的反应方式"。而且速度还不够快。
我更喜欢第二个选项,它需要更少的代码,而且似乎应该工作得更快(理论上(。
- 使用第二种方法是否会获得明显更好的性能?(记住,有元素的雷手(
- 这种方法会导致哪些具体问题?是的,是的,我知道代码不太透明,这不是它应该的样子。
- 你看到还有什么其他方法可以提高性能吗?我很乐意尝试更不像反应的建议,可能会考虑部分或全部放弃反应。
不可变的数据结构通过记忆和结构共享等技术解决您的性能问题。
您应该考虑使用不可变.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>
)
}
}