SDB_1998
SDB_1998

Reputation: 365

mapStateToProps is always re rendering in react-native

the mapStateToProps is always rendering for me, I was using this approach before with hooks const {contentLoading, ...exploreData} = useSelector(({explore}) => explore); but when I switched to this approach my component is always re rendering and I get always mapStateToProps in the console

const mapStateToProps = (state) => {
 console.log('mapStateToProps');
  const {contentLoading, ...exploreData} = state.explore;

  return {
    contentLoading,
    exploreData,
    token: state.auth.token,
  };
};

Upvotes: 1

Views: 176

Answers (1)

HMR
HMR

Reputation: 39270

It is re rendered because of this:

//explore data is re created every time
const { contentLoading, ...exploreData } = state.explore;

Try the following:

return state.explore;
//your component
function ExploreData({ contentLoading, ...exploreData }) {

Below is an example:

const { Provider, connect, useDispatch, useSelector } =
  ReactRedux;
const { createStore, applyMiddleware, compose } = Redux;

const initialState = {
  count: 0,
  explore: {
    contentLoading: true,
    other: 'props',
    forExplore: 'Data',
  },
};
//action types
const ADD = 'ADD';
//action creators
const add = () => ({
  type: ADD,
});
const reducer = (state, { type }) => {
  if (type === ADD) {
    return { ...state, count: state.count + 1 };
  }
  return state;
};
const composeEnhancers =
  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
  reducer,
  initialState,
  composeEnhancers(
    applyMiddleware(
      () => (next) => (action) => next(action)
    )
  )
);
function ExploreData({ contentLoading, ...exploreData }) {
  console.log('rendering ExploreData');
  return (
    <div>
      <div>{contentLoading}</div>
      <pre>{JSON.stringify(exploreData, undefined, 2)}</pre>
    </div>
  );
}
const ExploreDataContainer = connect((state) => {
  //a log here is normal every time state changes
  console.log(
    'container will re calculate props from state'
  );
  //explore data is re created every time
  return state.explore;
})(ExploreData);
const App = () => {
  const dispatch = useDispatch();
  const clickCount = React.useCallback(
    () => dispatch(add()),
    [dispatch]
  );
  const count = useSelector((state) => state.count);
  return (
    <div>
      <button onClick={clickCount}>{count}</button>
      <ExploreDataContainer />
    </div>
  );
};

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.2.0/react-redux.min.js"></script>


<div id="root"></div>

You can also use reselect that will not re create the object if state.explore did not change:

const selectExplore = (state) => state.explore;
const mapStateToProps = createSelector(
  [selectExplore],
  ({ contentLoading, ...exploreData }) => ({
    contentLoading,
    exploreData,
  })
);
const ComponentContainer = connect(mapStateToProps)(Component);

Reselect will also prevent you from writing duplicate implementation because you can compose selectors.

Upvotes: 1

Related Questions