How to use @connect type correctly when using React and TypeScript?

I struggled with this for the longest time. Today he sat down to get rid of anyone anyon the left, but failed.

import * as React from 'react';
import * as Redux from 'redux';
import { connect } from 'react-redux';
import { ReduxState } from './types';
import { syncItem, displayAlert } from './actionCreators';
import { SyncItemAction, DisplayAlertAction } from './actions';

// Props coming from Redux using `connect` and `mapStateToProps`
type AppData = {
    isSelected: boolean;
    isInEditMode: boolean;
};

// Action creators from `./actionCreators` all wrapped in `dispatch` using `connect` and `mapDispatchToProps`
type AppActions = {
    syncItem: (id: number) => SyncItemAction;
    displayAlert: (text: string) => DisplayAlertAction;
};

// Actual JSX attributes that will be required by the type system.
type AppProps = {
    id: number;
    name: string;
} & Partial<AppData> & Partial<AppActions>; // Making data and actions partial so that using <App /> in JSX doesn't yell.

// The component inner state.
type AppState = Partial<{
    temp: string;
}>;

@connect<AppData, AppActions, AppProps>(mapStateToProps, mapDispatchToProps)(App) // Or mapDispatchToPropsAlt
export default class App extends React.Component<AppProps, AppState> {
    constructor(props: AppProps) {
        super(props);
    }

    render() {
        return (
            <div>
                <h1>Hello, {this.props.name}! (#{this.props.id})</h1>
                {/* In the below, syncItem should take the new name, a detail… Also ID could be provided in `mapStateToProps` by using `ownProps`! */}
                Rename: <input value={this.state.temp} onChange={event => this.setState({ temp: event.target.value })} />
                <button onClick={_ => this.props.syncItem(this.props.id)}>Sync</button>
            </div>
        );
    }
}

function mapStateToProps(state: ReduxState, ownProps?: AppProps): AppData {
    return {
        isSelected: ownProps.id === state.selectedId,
        isInEditMode: state.isInEditMode
    };
}

function mapDispatchToProps(dispatch: Redux.Dispatch<ReduxState>, ownProps?: AppProps): AppActions {
    return {
        syncItem: (id: number) => dispatch(syncItem(id)),
        displayAlert: (text: string) => dispatch(displayAlert(text, ownProps.name))
    };
}

function mapDispatchToPropsAlt(dispatch: Redux.Dispatch<ReduxState>, ownProps?: AppProps): AppActions {
    return {
        syncItem,
        // Making this `null` because `displayAlert` above changes the signature by hiding the other parametr and taking it from `ownProps` - uncommon!
        displayAlert: null
    };
}

function Test() {
    // Only `id` and `name` is correctly required.
    return <App id={0} name={'test'} />;
}

In the above code, I get the following:

index.tsx(31,78): error TS2345: Argument of type 'typeof App' is not assignable to parameter of type 'ComponentClass<AppData & AppActions> | StatelessComponent<AppData & AppActions>'.
  Type 'typeof App' is not assignable to type 'StatelessComponent<AppData & AppActions>'.
    Type 'typeof App' provides no match for the signature '(props: AppData & AppActions & { children?: ReactNode; }, context?: any): ReactElement<any>'

I used type definitions in node_modules/@typesto come up with what I had above, and in a similar way I checked what it looks like ComponentClass<T>. He suggests (it seems to me) that there stateshould be a component {} | void, which I don’t understand why this is so, and also if I changed the code above to say <AppProps, void>or <AppProps, {}>, it doesn’t change the first error anyway.

How should I do it?

syncItemis simple function syncItem(id: number): SyncItemActionand SyncItemActionis interface SyncItemAction extends Redux.Action { id: number; }, likewise displayAlert.

: , , state.

+4
1

( , ). , Connected ( ).

ConnectedAppProps, ,

interface ConnectedAppProps {
    id: number;
    name: string;
}

AppProps

interface AppProps extends ConnectedAppProps, AppData, AppActions {
}

ConnectedComponent

const ConnectedApp: React.ComponentClass<ConnectedAppProps> = 
    connect<AppData,AppActions,AppProps>( mapStateToProps, mapDispatchToProps )( App )

Provider ConnectedAppProps

   <Provider store={store}>
       <ConnectedApp id={0} name={'test'} />
   </Provider>

mapStateToProp mapDispatchToProps, , "" AppProps ConnectedAppProps

+1

Source: https://habr.com/ru/post/1668448/


All Articles