Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How redux work when multiple components use parts of complex objects for it's source

I have object (this is one property array and as it is dynamic I can't split it to multiple properties) that I keep in reducer that looks something like this:

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

In one component I get it from store and access it like:

this.props.allElements

Then for each of it I render different component (pseudo code):

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

This way each component get only elements that is interested to.
My question is about performance and optimisation.

Is my approach any good?

I suffer some performance issues so checking if maybe my organisation
here is wrong and need advice if it can be improved.
I have some frequent updates sometimes map can be updated every second.

So if I update only map elements and pass it to action to update it will all this components run render again or just the one that watch map.


UPDATE

But to update map elements I need to get whole this object from store in action update only map elements and then pass whole updated object to reducer.
So only map is changed others are not.
Is react/redux smart enough to update only component that data is changed (map in my case).
Action:

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

Reducer:

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

In big component:

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
    )
}


Data example:

[
  {
    "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"
      }
    ]
  }
]
like image 514
1110 Avatar asked Oct 19 '17 18:10

1110


1 Answers

You can do the loop inside your thunk action and split the data into seperate reducers.

You Action could look something like this:

// 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
          });

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

Your separate reducers could look something like this:

// 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,
    }
  }
}

And then you can render it like this:

// 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>
  );
}

Edit
As a followup to your comment:

If I put allData to parent and pass allData.xyz to component in render when any data change parent will also reRender as it’s props is changed

Well, there is nothing wrong with Parent being re-rendered. when the render method of component runs it doesn't mean it will re-render to the physical DOM,It will update or override the object of the virtual DOM and then will check for differences between the physical and virtual DOM via the Reconciliation and The Diffing Algorithm to determine when, how and where should the physical DOM get updated.
It can update only certain attributes of DOM elements without re-render them, React Only Updates What’s Necessary.

There is also the option of React.PureComponent but i strongly advice against that in your case.

like image 64
Sagiv b.g Avatar answered Nov 09 '22 04:11

Sagiv b.g