首页 > 解决方案 > 更新 redux 商店时应用重新渲染

问题描述

每次我发送一个动作并更新我的商店时,我的整个应用程序都会重新呈现。我假设我的 connect/mapDispatchToProps 函数做错了什么?在 App.js 中{ ...actions }作为第二个参数传递给我的函数是否正确?connect

这是我的代码:

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

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

减速器/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 类似于 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;
    }
  }
}

标签: reactjsredux

解决方案


如果您在每次 redux 更新时有太多应用程序重新渲染的问题,它有助于使用更多连接的组件并限制传递给每个组件的状态量。我看到您正在将道具向下传播到每个页面,这很方便,但是重新渲染效率低下的常见原因。

<Home  {...this.props} />
<About {...this.props} />
<News  {...this.props} />

这可能会导致向这些组件中的每一个传递太多数据,并且每个 redux 操作都会导致整个页面重新渲染。

我看到的另一个潜在问题是您使用内联匿名函数作为路由的组件回调

<Route
    path={routes.ABOUT}
    component={() => (
        <About {...this.props} />
    )}
/>

我不确定 React Router 是如何在这里工作的,但一个潜在的问题是,每次路由器重新渲染时,这些匿名函数都会再次创建全新的。React 会将它们视为一个新组件并强制重新渲染。您可以通过将这些组件中的每一个都设置为一个连接组件来拉入它们自己的道具,然后像这样更新路由器来解决这个问题

<Route
    path={routes.ABOUT}
    component={ConnectedAbout}
/>

推荐阅读