next.js mapStateToProps、mapDispatchToProps 和 getInitialProps

next.js mapStateToProps, mapDispatchToProps and getInitialProps

我目前仍在尝试在使用 next.js 时围绕 redux 思考,我不确定下一步使用 redux 的最佳方式是什么。我习惯于将 mapDispatchToProps 用于我的操作,将 mapStateToProps 用于我的道具。经过一些研究,我现在像推荐的那样在 _app.js 中使用 next-redux-wrapper,但现在我正在为如何最好地获取我的道具和调度我的动作而奋斗。我看过一些示例和实践,现在有一个基于这些示例之一的计数器组件。

class Counter extends Component {
  increment = () => {
    const {dispatch} = this.props
    dispatch(incrementCount())
  }

  decrement = () => {
    const {dispatch} = this.props
    dispatch(decrementCount())
  }

  reset = () => {
    const {dispatch} = this.props
    dispatch(resetCount())
  }

  render () {
    const { count } = this.props
    return (
      <div>
        <h1>Count: <span>{count}</span></h1>
        <button onClick={this.increment}>+1</button>
        <button onClick={this.decrement}>-1</button>
        <button onClick={this.reset}>Reset</button>
      </div>
    )
  }
}

function mapStateToProps (state) {
  const {count} = state.counter;
  return {count};
}

export default connect(mapStateToProps)(Counter)

到目前为止,我看到的大多数示例都执行与此类似的操作,或者仅在 getInitialProps 中分派操作。有理由这样做而不使用 mapDispatchToProps 吗?

因为这项工作也非常好:

export default connect(null, {authenticate})(Signin);

在 getIntialProps 中调度动作似乎有一些缺点(或者我犯了一些错误),因为当道具改变时它们不会再次执行。在我的用户配置文件组件中,我根据来自 redux 存储的令牌获取当前用户,如下所示:

const Whoami = ({isAuthenticated, user}) => (
  <Layout title="Who Am I">
    {(isAuthenticated && user && <h3 className="title is-3">You are logged in as <strong className="is-size-2 has-text-primary">{user}</strong>.</h3>) ||
      <h3 className="title is-3 has-text-danger ">You are not authenticated.</h3>}
  </Layout>
);

Whoami.getInitialProps = async function (ctx) {
  initialize(ctx);

  const token = ctx.store.getState().auth.token;
  if(token) {
    const response = await axios.get(`${API}/user`, {headers: {
      authorization: token
    }});
    const user = response.data.user;
    return {
      user
    };
  }
    }

const mapStateToProps = (state) => (
  {isAuthenticated: !!state.auth.token}
);

export default connect(mapStateToProps)(Whoami);

这对于初始页面加载或在其中一个客户端导航时工作得很好,但是当令牌过期或我注销时页面不会反映没有重新加载或在没有我的 mapStateToProps 的情况下再次导航到那里。但是将关注点分散到 2 个单独的函数上似乎非常笨拙。但是我找不到更干净的方法。

提前致谢

关于mapDispatchToProps

最好使用 mapDispatchToProps 至少因为它更容易测试:您可以将模拟函数传递给您的组件。使用 this.props.dispatch 来分派一些导入的操作会更难。

关于getInitialProps

可能有帮助:

GetInitialProps: is provided by Next.js and it is NOT always triggered, so be careful with that, it happen when you wrap 1 component inside another. If the parent Component has GetInitialProps, the child's GetInitialProps will never be triggered, see this thread for more info.

在进一步尝试 next 之后,我找到了一些问题的答案。对于初始加载后数据未更改的页面,我可以通过将我的 thunk 重写为 return 调度来摆脱 mapStateToProps,并且只使用 getInitialProps ,如下所示:

export function fetchShow(id) {
  return (dispatch) => {
      dispatch({ type: actionTypes.FETCH_SHOW_REQUESTED,id});
      // we need to return the fetch so we can await it
      return fetch(`http://api.tvmaze.com/shows/${id}`)
          .then((response) => {
              if (!response.ok) {
                  throw Error(response.statusText);
              }
              //dispatch(itemsIsLoading(false));
              return response;
          })
          .then((response) => response.json())
          .then((data) =>      dispatch({type: actionTypes.FETCH_SHOW_SUCEEDED,id, show: data, time: Date.now() }))
          .catch(() => dispatch({ type: actionTypes.FETCH_SHOW_ERROR,id }));
  };
}

Post.getInitialProps = async function ({store, isServer, pathname, query}) {
  const { id } = query;
  const {show} = await store.dispatch(fetchShow(id));
  return {show};
}

对于数据应随商店变化而更新的页面,我还不确定。我目前的想法是尝试编写一个辅助函数,该函数将从 getInitialProps 和 mapStateToProps 调用以减少代码重复,但我还不确定。