簡體   English   中英

不兼容的道具:無效不能分配給 ThunkAction

[英]Incompatible props: void not assignable to ThunkAction

我有這個 redux thunk 動作,它在我的組件中實現,它通過容器組件連接到 redux。 我收到一個錯誤,即App本身的道具和傳遞給connect的道具不兼容。 看起來它期待一個 function 返回一個ThunkAction但正在接收一個返回void的 function 。

C:/Users/jtuzman/dev/NOUS/front_end/src/containers/AppContainer.tsx
Type error: Argument of type 'typeof App' is not assignable to parameter of type 'ComponentType<Matching<{ settingsReceived: boolean; error: Error | null; } & { changeParameter: (parameter: ParameterName, value: any) => ChangeParameterAction; reportError: (msg: string, type?: ErrorType) => ErrorAction; updateValuesFromBackend: (updatedValues: any) => void; }, AppProps>>'.
  Type 'typeof App' is not assignable to type 'ComponentClass<Matching<{ settingsReceived: boolean; error: Error | null; } & { changeParameter: (parameter: ParameterName, value: any) => ChangeParameterAction; reportError: (msg: string, type?: ErrorType) => ErrorAction; updateValuesFromBackend: (updatedValues: any) => void; }, AppProps>, any>'.
    Types of parameters 'props' and 'props' are incompatible.
      Type 'Matching<{ settingsReceived: boolean; error: Error | null; } & { changeParameter: (parameter: ParameterName, value: any) => ChangeParameterAction; reportError: (msg: string, type?: ErrorType) => ErrorAction; updateValuesFromBackend: (updatedValues: any) => void; }, AppProps>' is not assignable to type 'Readonly<AppProps>'.
        The types returned by 'updateValuesFromBackend(...)' are incompatible between these types.
          Type 'void' is not assignable to type 'ThunkAction<void, { meta: { ...; }; study: { ...; }; data: { ...; } | { ...; }; }, {}, MyAction>'.  TS2345

    18 |   mapStateToProps,
    19 |   mapDispatchToProps,
  > 20 | )(App);
       |   ^
    21 | 

我不明白這一點,首先是因為我的 function 確實返回了一個ThunkAction (我認為?)但主要是因為類型是typeof updateValuesFromBackend所以無論如何都不應該出現不匹配,對嗎?

BTW ThunkAction ,我的動作“應該”返回但“不是”,確實是返回 void 的 function 的別名。 (即, updateValuesFromBackend返回一個ThunkAction ,它確實是一個返回void的 function )

當我切換 tp class App extends Component<any>我當然可以運行該應用程序。

有誰知道我該如何解決這個問題?

動作/index.ts

export const updateValuesFromBackend = (updatedValues: any): MyThunkAction => {
  return (
    dispatch: ThunkDispatch<AppState, {}, MyAction>,
    getState: () => AppState
  ) => {
    const changes: any = {};

    Object.entries(updatedValues).forEach(([key, value]) => {
      const parameter = backEndSettingsKeys[key];
      if (!parameter) return;
      changes[parameter] = value;
    });

    // if we haven't received any settings yet, accept these settings
    if (true || !getState().meta.settingsReceived) {
      dispatch(setParameters(changes));
    } else {
      // Compare the received settings with the current settings.
      // If they match, we consider this a "success" response
      // after a settings update request.
      // If they don't, we consider this an error.
    }
  };
};

它在我的App組件中實現,該組件連接到我的 AppContainer 中的AppContainer

應用程序.tsx


export type AppProps = {
  settingsReceived: boolean,
  error: Error | null,
  changeParameter: typeof changeParameter,
  updateValuesFromBackend: typeof updateValuesFromBackend,
  reportError: typeof reportError
}

// class App extends Component<any> {
class App extends Component<AppProps> {

  settingsSubscription: W3CWebSocket;

  componentDidMount(): void {
    this.settingsSubscription = this.subscribeToSettings(urls.SETTINGS_WS);
  }

  subscribeToSettings(url: string): W3CWebSocket {
    let settingsSubscription = new W3CWebSocket(url);
    settingsSubscription.onopen = () => console.log('WebSocket Client Connected (settings)');
    settingsSubscription.onclose = () => console.log('WebSocket Client Disconnected (settings)');
    settingsSubscription.onmessage = (message: MessageEvent) => this.handleSettingsMessage(message);
    return settingsSubscription;
  }

  handleSettingsMessage(message: MessageEvent) {
    const updatedValues = JSON.parse(message.data);
    const { settingsReceived, reportError, updateValuesFromBackend } = this.props;

    if (settingsReceived) return reportError('Invalid settings received.');

    console.log('updating settings');
    updateValuesFromBackend(updatedValues);
  }

  render() {
    return this.props.error
      ? <ErrorWrapper/>
      : <InnerAppContainer/>
  }
}

export default App;

應用容器.tsx


const mapStateToProps = ({ meta }: AppState) => ({
  settingsReceived: meta.settingsReceived,
  error: meta.error
});

const mapDispatchToProps = ({
  changeParameter, reportError, updateValuesFromBackend
});

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

是的,問題特別是updateValuesFromBackend: typeof updateValuesFromBackend部分。

它的實際類型大致是() => thunkFunction => void 但是,由於dispatch和 thunk 的工作方式,您自己的組件獲得的綁定 function 實際上看起來像() => void

React-Redux 類型內部有一些魔力,試圖通過找出 thunk 本身返回的內容來“解決 thunk”(基本上去掉了=> thunkFunction部分)。 因此,您聲明updateValuesFromBackend的內容與connect將傳遞的內容之間存在不匹配。

我自己最近遇到了這個問題,並找到了一個非常簡潔的解決方案,我們很快就會在 React-Redux 文檔中正式推薦它。 我在這個 SO answer中描述了該方法,但為了完整起見,我將其粘貼在這里:

有一種巧妙的技術可以根據mapStatemapDispatch推斷connect的組合道具的類型。

@types/react-redux@7.1.2中有一個新的ConnectedProps<T>類型。 你可以像這樣使用它:

function mapStateToProps(state: MyState) {
    return {
        counter: state.counter
    };
}

const mapDispatch = {increment};

// Do the first half of the `connect()` call separately, 
// before declaring the component
const connector = connect(mapState, mapDispatch);

// Extract "the type of the props passed down by connect"
type PropsFromRedux = ConnectedProps<typeof connector>
// should be: {counter: number, increment: () => {type: "INCREMENT"}}, etc

// define combined props
type MyComponentProps = PropsFromRedux & PropsFromParent;

// Declare the component with the right props type
class MyComponent extends React.Component<MyComponentProps> {}

// Finish the connect call
export default connector(MyComponent)

請注意,如果它是 object,則這會正確推斷mapDispatch中包含的 thunk 動作創建者的類型,而typeof mapDispatch則不會。

更多細節:

非常感謝@markerikson 的精彩解釋。 在我的實現中,我想保持演示組件完全獨立並且不知道它的容器以及它與 Redux 存儲的連接。 因此,您在容器中定義連接類型並將其導入我的組件中的具體(和優秀)建議不適合我的架構。

您對connect如何剝離 function 簽名的調度部分的解釋確實是解決我的特定問題的方法:

export type AppProps = {
  settingsReceived: boolean,
  error: Error | null,
  changeParameter: typeof changeParameter,
  updateValuesFromBackend: (updatedValues: any) => void,
  reportError: typeof reportError
}

就個人而言,我認為最好將所有連接邏輯從組件中移出並放入 Saga 或自定義中間件。 這樣,當您收到 API 的響應時,您可以決定是否應該分派一個動作。

雖然它是基於 REST,而不是使用 WebSockets。 我建議看看redux-api-middleware是如何工作的,作為這種方法的一個例子。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM