Reputation: 365
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
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