Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/reactjs/27.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Reactjs 管理React中嵌套状态的好方法_Reactjs - Fatal编程技术网

Reactjs 管理React中嵌套状态的好方法

Reactjs 管理React中嵌套状态的好方法,reactjs,Reactjs,编辑:我重新写了这个问题,以澄清我的目的-感谢到目前为止帮助我磨练的人 我试图了解如何最好地管理React中复杂的嵌套状态,同时限制内容未更改的组件调用render()的次数 作为背景: 假设我在一个对象中同时具有“authors”和“publications”状态,如下所示: { 'authors' : { 234 : { 'name' : 'Alice Ames', 'bio' : 'Alice is the author of over ...',

编辑:我重新写了这个问题,以澄清我的目的-感谢到目前为止帮助我磨练的人

我试图了解如何最好地管理React中复杂的嵌套状态,同时限制内容未更改的组件调用
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)=>{
开关(动作类型){
厄普达案件