Reputation: 167
Everytime I dispatch an action and update my store, my entire app re-renders. I assume I'm doing anything wrong w/ my connect/mapDispatchToProps function? Is it right to pass { ...actions }
as a 2nd argument to my connect
function in App.js?
Here's my code:
class App extends Component {
componentDidMount() {
this.props.fetchPages(api.API_PAGES);
this.props.fetchPosts(api.API_POSTS);
window.addEventListener('resize', () => {
this.props.resizeScreen(window.innerWidth);
});
}
render() {
return (
<div>
{this.props.app.showIntro && <Intro {...this.props} endIntro={this.props.endIntro} />}
{!this.props.pages.isFetching && this.props.pages.data &&
<div>
<Navbar {...this.props} />
<Layout {...this.props}>
<Switch location={this.props.location}>
<Route
path={routes.HOME}
exact
component={() => (
<Home {...this.props} />
)}
/>
<Route
path={routes.ABOUT}
component={() => (
<About {...this.props} />
)}
/>
<Route
path={routes.NEWS}
exact
component={() => (
<News {...this.props} />
)}
/>
<Route
component={NotFound}
/>
</Switch>
</Layout>
</div>
}
</div>
);
}
}
function mapStateToProps(state) {
return {
app: state.app,
pages: state.pages,
posts: state.posts
};
}
export default withRouter(connect(
mapStateToProps,
{ ...actions }
)(App));
actions/index.js
export function resizeScreen(screenWidth) {
return {
type: types.RESIZE_SCREEN,
screenWidth
};
}
export function endIntro() {
return {
type: types.END_INTRO,
showIntro: false
};
}
export function toggleNav(bool) {
return {
type: types.TOGGLE_NAV,
navOpen: bool
};
}
export function toggleVideoPlayer(bool) {
return {
type: types.TOGGLE_VIDEO_PLAYER,
videoIsPlaying: bool
};
}
export function toggleScroll(bool) {
return {
type: types.TOGGLE_SROLL,
disableScroll: bool
};
}
// pages
function requestPages() {
return {
type: types.REQUEST_PAGES
};
}
function receivePages(data) {
return {
type: types.RECEIVE_PAGES,
data
};
}
// posts
function requestPosts() {
return {
type: types.REQUEST_POSTS
};
}
function receivePosts(data) {
return {
type: types.RECEIVE_POSTS,
data
};
}
// creators
export function fetchPages(path) {
return (dispatch, getState) => {
const { pages } = getState();
if (pages.isFetching) return;
dispatch(requestPages());
fetch(`${process.env.API_URL}${path}`)
.then(response => response.json())
.then(json => dispatch(receivePages(json)));
};
}
export function fetchPosts(path) {
return (dispatch, getState) => {
const { posts } = getState();
if (posts.isFetching) return;
dispatch(requestPosts());
fetch(`${process.env.API_URL}${path}`)
.then(response => response.json())
.then(json => dispatch(receivePosts(json)));
};
}
reducers/app.js:
const initialState = {
screenWidth: typeof window === 'object' ? window.innerWidth : null,
showIntro: true,
navOpen: false,
videoIsPlaying: false,
disableScroll: false
};
export default function app(state = initialState, action) {
switch (action.type) {
case RESIZE_SCREEN: {
return {
...state,
screenWidth: action.screenWidth
};
}
case TOGGLE_NAV: {
return {
...state,
navOpen: !state.navOpen
};
}
case END_INTRO: {
return {
...state,
showIntro: false
};
}
case TOGGLE_VIDEO_PLAYER: {
return {
...state,
videoIsPlaying: !state.videoIsPlaying
};
}
case TOGGLE_SCROLL: {
return {
...state,
disableScroll: !state.disableScroll
};
}
default: {
return state;
}
}
}
reducers/posts.js is similar to reducers/pages.js:
const initialState = {
isFetching: false
};
export default function posts(state = initialState, action) {
switch (action.type) {
case REQUEST_POSTS: {
return {
...state,
isFetching: true
};
}
case RECEIVE_POSTS: {
return {
...state,
isFetching: false,
data: action.data
};
}
default: {
return state;
}
}
}
Upvotes: 3
Views: 4052
Reputation: 7819
This is how redux should work: props are changing so the connected componentin re-redered.
You can:
shouldComponentUpdate
to limit rerender (note: this will also prevent subcomponents)Component
base class so you'll switch to shallow compareUpvotes: 1
Reputation: 14501
If you have an issue with too much of your app re-rendering with each redux update, it helps to use more connected components and limit the amount of state being passed to each one. I see that you're spreading props down into each page, this is convenient, but a common cause of inefficient re-renders.
<Home {...this.props} />
<About {...this.props} />
<News {...this.props} />
This could result in too much data being passed to each of these components, and each redux action causing the entire page to re-render.
Another potential issue that I see is that you're using an inline anonymous function as the component callback for your routes
<Route
path={routes.ABOUT}
component={() => (
<About {...this.props} />
)}
/>
I'm not exactly sure how React Router is working here, but a potential issue is that each time the router re-renders, those anonymous functions are created brand new again. React will see them as a new component and force a re-render. You can resolve this by making each of these a connected component that pulls in their own props, and then update the router like so
<Route
path={routes.ABOUT}
component={ConnectedAbout}
/>
Upvotes: 3