在带有 react-redux 的组件中使用动作的有效负载是错误的吗?

Is it wrong to use an action's payload inside a component with react-redux?

我想跟踪 API 我用 react-redux 发出的请求。为此,我想在操作中生成一个请求 ID,并通过有效负载将其传递给中间件和缩减器。然后,当我从我的组件分派操作时,我可以捕获请求 ID 并在请求进行时使用它来更新组件。

这是一些示例代码

export interface State {
  [requestId: number]: Request;
}
export interface Request {
  status: string;
  error?: string;
}

动作

export function createRequest(): Action {
  return {
    type: "request",
    payload: {
      requestId: Math.random () // Make a random Id here
    }
  };
}

减速器

export function createRequestReducer(state: State): State {
  return {
    ...state,
    ...{ state.payload.requestId: { status: "pending" } }
  }; 
}

组件

interface props {
  getRequestById: (id: number) => Request;
  createRequest: () => number;
}

const component = (props: testProps): JSX.Element => {
  const getRequestById = props.getRequestById;
  const [requestId, setRequestId] = useState(null);
  const [request, setRequest] = useState(null);

  useEffect(() => {
    if (requestId !== null) {
      setRequest(getRequestById(requestId));
    }
  }, [requestId]);

  return <div>The request status is {(request && request.status) || "Not started"}</div>;
}

function mapStateToProps(state: State) {
  return {
    getRequestById: (requestId: number): Request => {
      getRequestById(state, requestId)
    }
  };
}

function mapDispatchToProps(dispatch: Dispatch) {
  return {
    createRequest: (): number => {
      const action = createRequest();
      dispatch(action);
      return action.payload.requestId;
    }
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(component);

我希望这会起作用,但它可能是一个巨大的反模式。是否不建议这样做?如果是,是否有其他选择?

我认为您的方法技术上完全没问题。只有 “逻辑上” 进行一些更改可能有意义:

是的,“action”应该被发送到 reducer(而不是在其他任何地方使用,尽管这在技术上没有问题)。

但是你可以做什么:

1。单独的操作和值

在action creator函数里面,你想做什么就做什么。

因此您可以分别创建和使用 actionrequestId。 这在技术上与您所做的完全相同,但在逻辑上是分开的。

例如:

function createRequest(){
    const requestId = createUniqueId();
    const action = { type: "request", payload: { requestId: requestId } };
    return {
        requestId: requestId, // <-- request id independent of the action
        action: action,       // <-- action independent of the request id
    };
}

function mapDispatchToProps( dispatch: Dispatch ){
  return {
    createRequest: (): number => {
      const { requestId, action } = createRequest();
      dispatch( action );    // <-- action independent of the request id
      return requestId;      // <-- request id independent of the action
    }
  };
}

2。 “动作调度员”

我(显然还有其他人)喜欢使用我称之为“动作调度程序”的东西。 这是一个额外的步骤和更多的代码,但我认为当您习惯了这个概念时,它消除了必须将此类代码放在何处的任何疑虑。

例如:

// Create the action, and nothing else:
const createRequestActionCreator = function( requestId ){
  return { type: "request", payload: { requestId: requestId } };
};

// Preper some data needed to create the action:
const createRequestActionDispatcher = function( dispatch ){
  return function(){
    const requestId = createUniqueId();
    dispatch( createRequestActionCreator( requestId ) );
    return requestId;
  };
};

// 
function mapDispatchToProps( dispatch: Dispatch ) {
  return {
    createRequest: (): number => {
      const requestId = createRequestActionDispatcher( dispatch )();
      return requestId;
    }
  };
}

2.a

此外,如果需要,您可以直接将这样的“动作调度程序”作为道具传递。 在这种情况下,它基本上取代了 mapDispatchToProps 中的函数,但可以重复使用,例如:

function mapDispatchToProps( dispatch: Dispatch ) {
  return {
    createRequest: createRequestActionDispatcher( dispatch ),
  };
}

2.b

有些人更喜欢在这里使用 fat-arrow-function,我发现 更多 令人困惑,而不是更少,但一旦你习惯了这种模式,它看起来就更干净了:

const createRequestActionDispatcher = (dispatch: Dispatch) => (maybeSomeValue: MyType) => {
    const requestId = createUniqueId();
    dispatch( createRequestActionCreator( requestId ) );
    return requestId;
};

备注:

我通常更喜欢保持一致,为此我应该始终(或从不)使用这些“动作调度程序”, 但我发现大多数时候我并不需要它们,但有时我发现它们非常有用。 所以我实际上在某些地方使用 dispatch( myAction ) 而在其他地方使用 myActionDispatcher(value)(dispatch)。 我不喜欢那样,但效果很好,我也没有更好的主意。