更新Redux商店时,应用重新呈现

时间:2018-06-28 12:04:53

标签: reactjs redux

每次我发送动作并更新商店时,我的整个应用都会重新呈现。我假设我做错了w / connect / mapDispatchToProps函数?将{ ...actions }作为第二个参数传递给App.js中的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));

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与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;
    }
  }
}

2 个答案:

答案 0 :(得分:1)

如果在每次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}
/>

答案 1 :(得分:1)

这是redux的工作方式:道具正在更改,因此所连接的组件将被重新更改。

您可以:

  • 实施您的shouldComponentUpdate以限制重新呈现( note :这也将阻止子组件)
  • 使用PureComponent而不是Component基类,以便切换到浅表比较
  • 限制所连接道具的数量,也许您可​​以连接子组件。