当多个组件使用复杂对象的一部分来源时,redux如何工作

问题描述:

我有一个对象(这是一个属性数组,因为它是动态的,我不能将它拆分成多个属性)这样的:当多个组件使用复杂对象的一部分来源时,redux如何工作

[ 
    {type: list, elements: []} 
    {type: map, elements: []} 
    {type: wheelList, elements: []} 
    {type: chart, elements: []} 
    {type: dyna, elements: []} 
    ... 
] 

在一个组件,我得到它的存储和访问它想:

this.props.allElements 

然后,每它我呈现不同的组件(伪代码):

foreach (el in this.props.allElements) { 
    if (el == list) { 
     return <ListComponent elements={el.elements}> 
    } 
    if (el == map) { 
     return <MapComponent elements={el.elements}> 
    } 
    ... 
} 

这样每个组件只获取感兴趣的元素。
我的问题是关于性能和优化。

我的方法有什么好处吗?

我遇到一些性能问题,所以请检查我的组织
是否有问题,如果可以改进,则需要咨询。
我有一些频繁更新,有时地图可以每秒更新一次。

因此,如果我只更新地图元素并将其传递给动作以进行更新,则所有这些组件都将再次运行渲染或仅显示地图。


UPDATE

但更新map元素我需要得到整个这个objectstoreaction update only map元素,然后整个更新的对象传递给reducer
所以只有地图被改变别人不是。
反应/ redux是否足够智能以仅更新数据发生更改的组件(在我的情况下为地图)。
操作:

export function getScreenElements() : Action { 
    return function(dispatch) { 
    // get data from server here 
    // and do some calculations here 
    dispatch({ 
       type: 'ELEMENTS_RECEIVED', 
       allElements: data 
      }); 

减速机:

export default function (state:State = initialState, action:Action): State { 
    if (action.type === 'ELEMENTS_RECEIVED') { 
    return { 
     ...state, 
     allElements: action.allElements, 
    }; 

在大部件:

const mapStateToProps = state => ({ 
    allElements: state.main.allElements 
... 
class Main extends Component { 
... 
componentDidMount() { 
    // here I actually make a call to get data 
} 
... 
render() { 
    return (
     // Here is the big code 
     // Loop over props.allElements 
     // and render it in different way based on data inside each element 
    ) 
} 


数据例如:

[ 
    { 
    "type": "list", 
    "count": 99, 
    "elements": [ 
     { 
     "id": 1, 
     "name": "test" 
     }, 
     { 
     "id": 2, 
     "name": "test1" 
     } 
    ] 
    }, 
    { 
    "type": "map", 
    "count": 99, 
    "elements": [ 
     { 
     "id": 3, 
     "name": "test" 
     }, 
     { 
     "id": 4, 
     "name": "test1" 
     } 
    ] 
    }, 
    { 
    "type": "list", 
    "count": 99, 
    "elements": [ 
     { 
     "id": 5, 
     "name": "test" 
     }, 
     { 
     "id": 6, 
     "name": "test1" 
     } 
    ] 
    }, 
    { 
    "type": "list", 
    "count": 99, 
    "elements": [ 
     { 
     "id": 7, 
     "name": "test" 
     }, 
     { 
     "id": 8, 
     "name": "test1" 
     } 
    ] 
    } 
] 
+0

为什么你不把它分裂成不同的减速器呢? –

+0

我真的会从中受益吗?这是我从服务器获得的对象,它非常动态。这就是为什么我要求解释如何应对这种情况。它会只更新一个还是全部? – 1110

+0

好吧,我认为在'render'中做这件事不太可取,那么在一个'thunk'(动作创建器)内循环并将每个'type'分配给不同的'reducer'是不太可取的。 –

您可以在thunkaction中执行循环,并将数据拆分为单独的reducers

你的行动可能看起来像这样:

// action thunk 
export function getScreenElements() { 
    return function (dispatch) { 
    // get data from server here 
    const dataFromServer = fetch('url'); 
    // do your looping here and dispatch each data acording to its type 
    dataFromServer.map(obj => { 
     switch (obj.type) { 
     case 'list': 
      dispatch({ 
      type: 'LIST_RECEIVED', 
      listElements: obj 
      }); 

     case 'map': 
      dispatch({ 
      type: 'MAP_RECEIVED', 
      mapElements: obj 
      }); 

     // .... 
     } 
    }); 
    } 
} 

你单独减速可能是这个样子:

// map reducer 
export default function map(state = initialState, action) { 
    if (action.type === 'MAP_RECEIVED') { 
    return { 
     ...state, 
     mapElements: action.mapElements, 
    } 
    } 
} 


// list reducer 
export default function list(state = initialState, action) { 
    if (action.type === 'LIST_RECEIVED') { 
    return { 
     ...state, 
     listElements: action.listElements, 
    } 
    } 
} 

然后你就可以使它像这样:

// inside your component render mehtod 
render(){ 
    const { list, map } = this.props; // redux data passed as props from the seperate reducers 
    return (
    <div> 
     <ListComponent elements={list}> 
     <MapComponent elements={map}> 
    </div> 
); 
} 

编辑
作为随访到您的评论:

如果我把ALLDATA于母公司并通过allData.xyz到组件中呈现 当任何数据变化父也将重新描绘,因为它的道具是 改变

那么,父母被重新渲染没有任何错误。 componentrender方法运行并不意味着它会重新渲染到实体DOM,它将更新或覆盖虚拟对象DOM然后将通过Reconciliation and The Diffing Algorithm检查物理和虚拟DOM之间的差异以确定何时,如何以及在哪里应该更新物理DOM
它可以只更新DOM元素的某些属性而不重新渲染它们,React Only Updates What’s Necessary

也有React.PureComponent的选项,但我坚决反对你的情况。

+0

所以这样。如果地图更新列表不会重新渲染并且相反。 – 1110

+0

是的,如果内部没有新的道具或状态改变,不会重新渲染 –

+0

我明白了这一点。但会接受答案。如果我得到3个列表和3个地图作为响应,该解决方案无法帮助我。 Thean每个将需要不同的减速器。或者我错了,这可以应用于此。 – 1110

在您的数据示例中,有多个具有相同类型的项目(例如:列表)。

Redux不推荐这种嵌套对象,因为您将无法优化渲染或优化数据失效。

之一以优化的方式就是击败你的数据,以便看起来像这样:

{ 
    isFetching: false, 
    didInvalidate: false, 
    data: [ 
    { type: 'list', id: 1, name: 'test' }, 
    { type: 'list', id: 2, name: 'test1' }, 
    { type: 'map', id: 3, name: 'test' }, 
    { type: 'map', id: 4, name: 'test1' }, 
    { type: 'list', id: 5, name: 'test' }, 
    { type: 'list', id: 6, name: 'test1' }, 
    { type: 'list', id: 7, name: 'test' }, 
    { type: 'list', id: 8, name: 'test1' }, 
    ] 
} 

app.js

class App extends React.Component { 
    render() { 
    return (
     <div> 
     <ListComponent /> 
     <MapComponent /> 
     </div> 
    ); 
    } 
} 

selectors.js

使用reselect图书馆为了避免无用的重投。

此选择器是共享的,因为您可以将它用于ListComponent,MapComponent和所有其他组件以查找与您的类型相对应的项目。

import { createSelector } from 'reselect'; 

export const typeElementsSelector = createSelector(
    state => state.allElements.data, 
    (allElements, props) => allElements.find(element => element.type === props.type), 
); 

ListComponent。js

这里的诀窍是只传递元素id并让孩子检查何时重置。

注意ListComponent将重新呈现仅且仅当没有在listElements值进行任何修改。 实例:

  • 现在有一种类型“列表”的一个新的项
  • 有被修改类型“列表”
  • 一个当前的“列表”项中的一个被移除的项

代码:

import React from 'react'; 
import { connect } from 'react-redux'; 
import { createSelector } from 'reselect'; 

import { typeElementsSelector } from './selectors'; 

class ListComponent extends React.Component { 
    render() { 
    if (!this.props.listElements.length) return (<div>Empty</div>); 

    return (
     <div> 
     <h2>{this.props.listElements.length} list elements</h2> 
     <div> 
      {this.props.listElements.map(element => (
      <ListComponentItem key={`list-${element.id}`} id={element.id} /> 
     ))} 
     </div> 
     </div> 
    ); 
    } 
} 

const mapStateToProps = (reducers, props) => ({ 
    listElements: typeElementsSelector(reducers, props), 
}); 

export default connect(mapStateToProps)(ListComponent); 

ListComponentItem.js

您现在可以显示任何你想要的东西。 该组件将只且仅当在减速机这项已被修改

import React from 'react'; 
import { connect } from 'react-redux'; 
import { createSelector } from 'reselect'; 

class ListComponentItem extends React.Component { 
    render() { 
    return <div>{this.props.name}</div> 
    } 
} 

const elementSelector = createSelector(
    state => state.allElements.data, 
    (allElements, props) => allElements.find(element => element.id === props.id), 
); 

const mapStateToProps = (reducers, props) => ({ 
    element: elementSelector(reducers, props), 
}); 

export default connect(mapStateToProps)(ListComponentItem); 

的另一种方法是创建为每种类型的一个减速器,并使用相同的逻辑如上重新呈现。

{ 
    listElements: { 
    isFetching: false, 
    didInvalidate: false, 
    data: [ 
     { id: 1, name: 'test' }, 
     { id: 2, name: 'test1' }, 
     { id: 5, name: 'test' }, 
     { id: 6, name: 'test1' }, 
     { id: 7, name: 'test' }, 
     { id: 8, name: 'test1' }, 
    ] 
    }, 
    mapElements: { 
    isFetching: false, 
    didInvalidate: false, 
    data: [ 
     { id: 3, name: 'test' }, 
     { id: 4, name: 'test1' }, 
    ] 
    }, 
} 

请记住,您的API可能会以某种方式向您发送格式化的数据,但您可以根据需要重新格式化它们。

一个有趣的文章谈论这种优化:https://medium.com/dailyjs/react-is-slow-react-is-fast-optimizing-react-apps-in-practice-394176a11fba

如果你想走得更远终极版与数据标准化的最佳实践:https://github.com/paularmstrong/normalizr

希望它能帮助。