React Redux和组件更新优化

时间:2016-09-24 22:59:23

标签: reactjs redux react-redux

代码:

根组件

    <Provider store={Store}>
       <AppLayoutContainer>
           {this.props.template}
       </AppLayoutContainer>
    </Provider>

AppLayoutContainer

....some code

  return(
    <div className={`AppLayoutContainer`}>
       <Modal isOpen={isModalShown} .... />
       <div>{children}</div>
     </div>
  )

....some code

function mapStateToProps(state) {
  const { app } = state;
  return {
    isModalShown: app.isModalShown
   }
}

export default connect(mapStateToProps)(AppLayoutContainer);

问题:

每当Applayoutcontainer从Redux获得isModalShown时,我的整个应用都会重新渲染,而且非常糟糕。我只需要重新渲染模态组件而不要触摸我的children。我认为使用ShouldComponentUpdate方法可行,但我真的不明白该怎么做。

有什么想法吗?我知道有人在他的项目中做过这件事并可以为此提出一些最佳实践建议。感谢

UPDATE:

阅读所有答案,是的,将Modal与Store连接是一个很好的解决方案, 但是,如果

1)我希望Modal组件是纯粹的(可重用的)或组件吗?

2)我需要在AppLayoutContainer中使用isModalShown吗?

return(
    <div className={`AppLayoutContainer`}>
       <Modal isOpen={isModalShown} .... />
       <div>{children}</div>
     </div>
  )

更新2.0

感谢您的回答,但我该怎么办

当我显示Modal组件时,我需要模糊我的所有内容的主要问题,因为我知道blur()只能应用于要工作的内容(而不是覆盖我的内容的绝对位置div) )并按照这样做

return(
        <div className={`AppLayoutContainer`}>
           <Modal isOpen={isModalShown} .... />
           <div className={isModalShown ? 'blured' : null }>{children}</div>
         </div>
      )

但每当我出现Modal时,我的整个应用都会重新呈现,我需要冻结它。为child构建容器并且在shouldcomponentupdate执行import tensorflow as tf import numpy as np import tempfile tmp_filename = 'tf.tmp' sequences = [[1, 2, 3], [1, 2], [3, 2, 1]] label_sequences = [[0, 1, 0], [1, 0], [1, 1, 1]] def make_example(input_sequence, output_sequence): """ Makes a single example from Python lists that follows the format of tf.train.SequenceExample. """ example_sequence = tf.train.SequenceExample() # 3D length sequence_length = len(input_sequence) example_sequence.context.feature["length"].int64_list.value.append(sequence_length) input_characters = example_sequence.feature_lists.feature_list["input_characters"] output_characters = example_sequence.feature_lists.feature_list["output_characters"] for input_character, output_character in zip(input_sequence, output_sequence): if input_sequence is not None: input_characters.feature.add().int64_list.value.append(input_character) if output_characters is not None: output_characters.feature.add().int64_list.value.append(output_character) return example_sequence # Write all examples into a TFRecords file def save_tf(filename): with open(filename, 'w') as fp: writer = tf.python_io.TFRecordWriter(fp.name) for sequence, label_sequence in zip(sequences, label_sequences): ex = make_example(sequence, label_sequence) writer.write(ex.SerializeToString()) writer.close() def read_and_decode_single_example(filename): filename_queue = tf.train.string_input_producer([filename], num_epochs=None) reader = tf.TFRecordReader() _, serialized_example = reader.read(filename_queue) context_features = { "length": tf.FixedLenFeature([], dtype=tf.int64) } sequence_features = { "input_characters": tf.FixedLenSequenceFeature([], dtype=tf.int64), "output_characters": tf.FixedLenSequenceFeature([], dtype=tf.int64) } return serialized_example, context_features, sequence_features save_tf(tmp_filename) ex,context_features,sequence_features = read_and_decode_single_example(tmp_filename) context_parsed, sequence_parsed = tf.parse_single_sequence_example( serialized=ex, context_features=context_features, sequence_features=sequence_features ) sequence = tf.contrib.learn.run_n(sequence_parsed, n=1, feed_dict=None) #check if the saved data matches the input data print(sequences[0] in sequence[0]['input_characters']) 的解决方案并不好,React如何比较整个组件?它只比较道具中不可变的字段。

任何解决方案?

3 个答案:

答案 0 :(得分:4)

您应该做的不是通过isModal的{​​{1}}函数中的AppLayoutContainer,而是通过mapStateToProps connect本身。{/ p >

Modal更改的任何结果都会导致mapStateToProps ed组件重新呈现。在您的情况下,connect mapStateToProps转到connect会返回AppLayoutContainer的值,这会导致isModalShown重新呈现。

如果您只想重新呈现AppLayoutContainer本身,那么只需

Modal

在您的export default connect(state => ({isOpen: state.app.isModalShown}))(Modal) 文件中,如果您更喜欢,也可以在单独的文件中。

同时从Modal移除isModalShown道具,因为您不需要,因为将其留在那里意味着AppLayoutContainer每次都会重新渲染AppLayoutContainer更改(这是您要修复的内容)。

有关问题更新的

更新

问题1:

您可以拥有纯isModalShown

Modal

然后,您可以为特定用途制作特定的模态:

export default function Modal(props) {
  return <span>Open={props.isOpen}</span>;
}

对于各种单独的模态,您可以多次执行此操作。事实上,这就是使用纯组件的好主意。

问题2:

如果您想在import Modal from './modal'; import { connect } from 'react-redux'; export default const AppLayoutModal = connect(state => ({isOpen: state.app.isModalShown}))(Modal); 中使用isModalShown,那么您有一些选择:

  • 只需使用它。 React总是呈现整个组件。因为它只是一种渲染方法。它会更新DOM,如果有的话,更精细,但如果你想要更少的东西在更改时渲染,那么由你决定使你的渲染函数变小。如果你把组件放大,那么整件事就会重新出现。
  • 您可以将组件切割成较小的组件,以便可以单独渲染它们。您可以将一个小组件直接通过AppLayoutContainer直接使用isModalShown,这样大connect就不会改变。子组件可以独立于其父组件进行更新。

更新2

要模糊整个应用,只需在应用中添加一个类。子组件不会重新渲染。 Redux会阻止孩子渲染,因为它会注意到孩子们没有使用AppLayoutContainer。生成的React元素与前一个渲染完全相同的对象(引用)。 React看到它是相同的元素,并且知道不更新整个树。这是有效的,因为React元素是不可变的。您的整个应用程序都不会重新呈现。实际上,React将在类属性中添加或删除该类。

如果您的孩子不使用Redux来阻止渲染器在树上行走,那么您可以将子项包装在Redux连接组件中。或者,您可以使用isModalShown来阻止孩子的渲染。

Bare React要求您从根目录向下发送数据。 Redux要求您将数据注入树中所需的位置。后者的好处是没有其他任何东西需要重新渲染。如果没有Redux,除了组件使用shouldComponentUpdate停止传播时,每个更改都会重新呈现整个树 。但是,这不仅会为当前组件停止渲染,也会为所有子组件停止渲染。 Redux消除了这种情况,因为子组件可以在没有父母的合作或知识的情况下更新其道具。

简而言之:到处都使用Redux来提高速度。

答案 1 :(得分:1)

这里有不同的策略。

  1. 在您的子组件中使用shouldComponentUpdate(在这种情况下非常杂乱且不优雅);
  2. 将Modal组件连接到商店,而不是AppLayoutContainer;
  3. isModalShown存储在Modal状态,而不是全局状态。
  4. 如果可能,最后的解决方案往往是更合适的恕我直言。您将UI状态保留在相关组件中,因为您不需要经常在其他组件中了解这一点。

    class Modal extends Component {
      state = {
        isModalShown: false,
      };
    
      render() {
        // ...
      }
    }
    

    如果您需要在其他地方了解isModalShown,则应该连接Modal组件而不是AppLayoutContainer

    export default connect((state) => ({
      isModalShown: state.app.isModalShown,
    }))(Modal)
    

答案 2 :(得分:0)

我认为DDS和Anthony Dugois有权利,但如果这些不是您正在寻找的答案,您可以随时将children传递到新组件中,并使用{{1}那里。