Reactjs 管理React中嵌套状态的好方法
编辑:我重新写了这个问题,以澄清我的目的-感谢到目前为止帮助我磨练的人 我试图了解如何最好地管理React中复杂的嵌套状态,同时限制内容未更改的组件调用Reactjs 管理React中嵌套状态的好方法,reactjs,Reactjs,编辑:我重新写了这个问题,以澄清我的目的-感谢到目前为止帮助我磨练的人 我试图了解如何最好地管理React中复杂的嵌套状态,同时限制内容未更改的组件调用render()的次数 作为背景: 假设我在一个对象中同时具有“authors”和“publications”状态,如下所示: { 'authors' : { 234 : { 'name' : 'Alice Ames', 'bio' : 'Alice is the author of over ...',
render()
的次数
作为背景:
假设我在一个对象中同时具有“authors”和“publications”状态,如下所示:
{
'authors' : {
234 : {
'name' : 'Alice Ames',
'bio' : 'Alice is the author of over ...',
'profile_pic' : 'http://....'
},
794 : {
'name' : 'Bob Blake',
'bio' : 'Hailing from parts unknown, Bob...',
'profile_pic' : 'http://....'
},
...more authors...
},
'publications' : {
539 : {
'title' : 'Short Story Vol. 2',
'author_ids' : [ 234, 999, 220 ]
},
93 : {
'title' : 'Mastering Fly Fishing',
'author_ids' : [ 234 ]
},
...more publications...
}
}
在这个人为的例子中,州有两个主要区域,由作者
和出版物
键访问
authors
键指向在作者ID上键入的对象,该ID指向包含一些作者数据的对象
publications
键指向在出版物ID上键入的对象,该对象具有一些出版物数据和作者数组
假设我的状态在App
组件中,其子组件如下所示:
...
<App>
<AuthorList authors={this.state.authors} />
<PublicationList authors={this.state.authors} publications={this.state.publications} />
</App>
...
...
class AuthorList extends React.Component {
render() {
let authors = this.props.authors;
return (
<div>
{ Object.keys( authors ).map( ( author_id ) => {
return <Author author={authors[author_id]} />;
}
</div>
);
}
}
...
...
class PublicationList extends React.Component {
render() {
let publications = this.props.publications;
let authors = this.props.authors;
return (
<div>
{ Object.keys( publications ).map( ( publication_id ) => {
return <Publication publication={publications[publication_id]} authors=authors />;
}
</div>
);
}
}
...
。。。
React组件的render()
函数将在其状态或其任何父级的状态发生更改时被调用,而不管该状态更改是否与该组件的道具有关。可以使用shouldComponentUpdate更改此行为
人们是如何处理上述复杂状态的——似乎并不是每次状态更改时对大量组件调用render()都是一个好的解决方案(即使最终渲染的对象是相同的,因此实际DOM不会发生任何更改)。组件状态应该只包含内部状态值
您应该考虑使用存储多个组件中所需的更复杂的状态 您应该使用,per。这提供了一种更新部分状态的机制,并可以在尽可能少的重复的情况下为您处理任何所需的克隆
这允许您执行以下操作:
this.setState(update(this.state, { authors: { $set: { ... } } }));
它只会重新渲染受更改影响的组件。以下是一种使用对象扩展语法以可读的方式高效完成此操作的方法
let state = {
authors : {
...this.state.authors,
[ givenId ] : {
...this.state.authors[ givenID ],
bio : newValue
}
}
}
this.setState(state)
请记住,在jsx中映射项目时,必须传递一个“键”作为道具
这主要是因为,React所做的协调(React的“diffing”算法用于检查发生了什么变化)检查映射jsx的键(大致命名为jsx)
无论如何,在reacts state/setState或redux中管理状态与“协调”无关
在这两种情况下,都可以使用“对象扩展语法”语法更改嵌套数据的部分
其余部分只需将“相同”键传递给映射的jsx。因此,尽管react重新加载,但它不会尝试对不必要的部分进行dom更新,这是非常昂贵的 我认为使用将使您的应用程序更高效、更易于管理
有一个称为Redux存储的全局状态,它允许您的任何组件订阅存储的一个片段,并在这些数据发生更改时重新渲染
在您的示例中,实现它的redux方式是您的AuthorList
组件将订阅状态。authors
对象,如果AuthorList
组件内部或外部的任何组件更新了状态。authors
,则只有AuthorList
组件将重新渲染(以及那些订阅它的人)。感谢迪托瑞和达维奥斯给我指出Redux
下面是一个示例应用程序(有大量的切角操作,但它展示了使用Redux将组件与与其无关的状态更改隔离开来的技术)
在本例中,对id为1的author Alice所做的更改不会导致不依赖Alice的组件调用其render()
这是因为Redux为其连接的react组件提供的shouldComponentUpdate
会评估道具是否已更改以及相关状态是否已更改
需要预先警告的是,Redux在这里的优化很肤浅。要确定wither是否跳过render()
Redux的shouldComponentUpdate
检查:
- 新旧道具相互关联
- 或者,如果它们没有相同的键,并且这些键的值彼此之间是
=
因此,它可能会导致为其值在逻辑上仍然相等的组件调用render()
,但是这些组件的道具及其第一级键与==
不相等。请参阅:
还要注意,为了防止调用Author
的“dumb”组件render()
,我必须connect()
将它连接到Redux,以启用Redux的shouldComponentUpdate
逻辑,即使该组件对状态根本不做任何操作,只是读取它的道具
import ReactDOM from 'react-dom';
import React from 'react';
import { Provider, connect } from 'react-redux';
import { createStore, combineReducers } from 'redux';
import update from 'immutability-helper';
const updateAuthor = ( author ) => {
return ( {
type : 'UPDATE_AUTHOR',
// Presently we always update alice and not a particular author, so this is ignored.
author
} );
};
const updateUnused = () => {
return ( {
type : 'UPDATE_UNUSUED',
date : Date()
} );
};
const initialState = {
'authors': {
1: {
'name': 'Alice Author',
'bio': 'Alice initial bio.'
},
2: {
'name': 'Bob Baker',
'bio': 'Bob initial bio.'
}
},
'publications': {
1 : {
'title' : 'Two Authors',
'authors' : [ 1, 2 ]
},
2 : {
'title' : 'One Author',
'authors' : [ 1 ]
}
}
};
const initialDate = Date();
const reduceUnused = ( state=initialDate, action ) => {
switch ( action.type ) {
case 'UPDATE_UNUSED':
return action.date;
default:
return state;
}
};
const reduceAuthors = ( state=initialState, action ) => {
switch ( action.type ) {
case 'UPDATE_AUTHOR':
let new_bio = state.authors['1'].bio + ' updated ';
let new_state = update( state, { 'authors' : { '1' : { 'bio' : {$set : new_bio } } } } );
/*
let new_state = {
...state,
authors : {
...state.authors,
[ 1 ] : {
...state.authors[1],
bio : new_bio
}
}
};
*/
return new_state;
default:
return state;
}
};
const testReducers = combineReducers( {
reduceAuthors,
reduceUnused
} );
const mapStateToPropsAL = ( state ) => {
return ( {
authors : state.reduceAuthors.authors
} );
};
class AuthorList extends React.Component {
render() {
return (
<div>
{ Object.keys( this.props.authors ).map( ( author_id ) => {
return <Author key={author_id} author_id={author_id} />;
} ) }
</div>
);
}
}
AuthorList = connect( mapStateToPropsAL )(AuthorList);
const mapStateToPropsA = ( state, ownProps ) => {
return ( {
author : state.reduceAuthors.authors[ownProps.author_id]
} );
};
class Author extends React.Component {
render() {
if ( this.props.author.name === 'Bob Baker' ) {
alert( "Rendering Bob!" );
}
return (
<div>
<p>Name: {this.props.author.name}</p>
<p>Bio: {this.props.author.bio}</p>
</div>
);
}
}
Author = connect( mapStateToPropsA )( Author );
const mapStateToPropsPL = ( state ) => {
return ( {
authors : state.reduceAuthors.authors,
publications : state.reduceAuthors.publications
} );
};
class PublicationList extends React.Component {
render() {
console.log( 'Rendering PublicationList' );
let authors = this.props.authors;
let publications = this.props.publications;
return (
<div>
{ Object.keys( publications ).map( ( publication_id ) => {
return <Publication key={publication_id} publication={publications[publication_id]} authors={authors} />;
} ) }
</div>
);
}
}
PublicationList = connect( mapStateToPropsPL )( PublicationList );
class Publication extends React.Component {
render() {
console.log( 'Rendering Publication' );
let authors = this.props.authors;
let publication_authors = this.props.publication.authors.reduce( function( obj, x ) {
obj[x] = authors[x];
return obj;
}, {} );
return (
<div>
<p>Title: {this.props.publication.title}</p>
<div>Authors:
<AuthorList authors={publication_authors} />
</div>
</div>
);
}
}
const mapDispatchToProps = ( dispatch ) => {
return ( {
changeAlice : ( author ) => {
dispatch( updateAuthor( author ) );
},
changeUnused : () => {
dispatch( updateUnused() );
}
} );
};
class TestApp extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<p>
<span onClick={ () => { this.props.changeAlice( this.props.authors['1'] ); } }><b>Click to Change Alice!</b></span>
</p>
<p>
<span onClick={ () => { this.props.changeUnused(); } }><b>Click to Irrelevant State!</b></span>
</p>
<div>Authors:
<AuthorList authors={this.props.authors} />
</div>
<div>Publications:
<PublicationList authors={this.props.authors} publications={this.props.publications} />
</div>
</div>
);
}
}
TestApp = connect( mapStateToPropsAL, mapDispatchToProps )( TestApp );
let store = createStore( testReducers );
ReactDOM.render(
<Provider store={store}>
<TestApp />
</Provider>,
document.getElementById( 'test' )
);
从“react dom”导入ReactDOM;
从“React”导入React;
从'react redux'导入{Provider,connect};
从'redux'导入{createStore,combinereducer};
从“不变性助手”导入更新;
const updateAuthor=(作者)=>{
报税表({
键入:“更新作者”,
//目前,我们总是更新alice,而不是某个特定的作者,因此忽略了这一点。
作者
} );
};
const updateUnused=()=>{
报税表({
键入:“更新\未发布”,
日期:日期()
} );
};
常量初始状态={
“作者”:{
1: {
'姓名':'爱丽丝作者',
‘个人简历’:‘爱丽丝最初的个人简历’
},
2: {
“姓名”:“鲍勃·贝克”,
“个人简历”:“鲍勃的初始个人简历。”
}
},
“出版物”:{
1 : {
"标题":"两位作者",,
“作者”:[1,2]
},
2 : {
“标题”:“一位作者”,
“作者”:[1]
}
}
};
const initialDate=日期();
const reduceUnused=(state=initialDate,action)=>{
开关(动作类型){
厄普达案件