reactjs - React redux typescript: connect har manglende typefeil


Jeg prøver å få en react-redux-app til å fungere med typescript, men jeg blir stadig møtt med den samme feilen. Følgende kode kompilerer og gir forventet resultat

// State definition
interface HelloWorldState {
    clickCount: number
}
interface AppState extends HelloWorldState {}


// Props definitions
interface HelloWorldProps {
    count: number
}


// Actions
const CLICK = 'CLICK';
const click = () => {return {type:CLICK}};


// Reducers
function clickCount(state:number = 0, action:Action) {
    if (typeof state === 'undefined') {
        return 0;
    }
    switch (action.type) {
        case CLICK:
            return state + 1;
        default:
            return state;
    }
}
let rootReducer = combineReducers({
    clickCount
});


// Store
let store = createStore(rootReducer);


// Components
class HelloWorld extends React.Component<any, any> {
    render() {
        return <div onClick={this.handleClick.bind(this)}>Hello world "{this.props.count}"</div>
    }

    handleClick() {
        store.dispatch(click())
    }
}


// Container components
const mapStateToProps = (state:AppState):HelloWorldState => {
    return Immutable.fromJS({
        count: state.clickCount
    })
};
const ConnectedHelloWorld = connect(
    mapStateToProps
)(HelloWorld);

render(
    <Provider store={store}>
        <ConnectedHelloWorld/>
    </Provider>,
    container
);

Flott! Men jeg bruker TypeScript, fordi jeg vil få typekontroller ved kompilering. Det viktigste å typekontrollere er state og props. Så i stedet for class HelloWorld extends React.Component<any, any>, vil jeg gjøre class HelloWorld extends React.Component<HelloWorldProps, any>. Når jeg gjør dette, får jeg imidlertid følgende kompilasjonsfeil fra kallet til render

TS2324:Property 'count' is missing in type 'IntrinsicAttributes & IntrinsicClassAttributes<HelloWorld> & HelloWorldProps & { children?: React...'.

Jeg forstår egentlig ikke hvorfor. count ER til stede i HelloWordProps-definisjonen, og den blir levert av redukteren, så jeg burde vel være i orden, ikke sant? Lignende spørsmål har antydet at det er et inferensproblem, og at jeg burde erklære typen på kallet til connect, men jeg klarer ikke å finne ut hvordan.

package.json

{
  "name": "reacttsx",
  "scripts": {
    "build": "webpack"
  },
  "devDependencies": {
    "ts-loader": "^1.3.3",
    "typescript": "^2.1.5",
    "webpack": "^1.14.0",
    "typings": "^2.1.0"
  },
  "dependencies": {
    "es6-promise": "^4.0.5",
    "flux": "^3.1.2",
    "immutable": "^3.8.1",
    "isomorphic-fetch": "^2.2.1",
    "jquery": "^3.1.1",
    "react": "^15.4.2",
    "react-dom": "^15.4.2",
    "react-redux": "^5.0.2",
    "redux": "^3.6.0",
    "redux-logger": "^2.7.4",
    "redux-thunk": "^2.2.0"
  }
}

typings.json

{
  "dependencies": {
    "flux": "registry:npm/flux#2.1.1+20160601175240",
    "immutable": "registry:npm/immutable#3.7.6+20160411060006",
    "react": "registry:npm/react#15.0.1+20170104200836",
    "react-dom": "registry:npm/react-dom#15.0.1+20160826174104",
    "react-redux": "registry:npm/react-redux#4.4.0+20160614222153",
    "redux-logger": "registry:dt/redux-logger#2.6.0+20160726205300",
    "redux-thunk": "registry:npm/redux-thunk#2.0.0+20160525185520"
  },
  "globalDependencies": {
    "es6-promise": "registry:dt/es6-promise#0.0.0+20160726191732",
    "isomorphic-fetch": "registry:dt/isomorphic-fetch#0.0.0+20170120045107",
    "jquery": "registry:dt/jquery#1.10.0+20170104155652",
    "redux": "registry:dt/redux#3.5.2+20160703092728",
    "redux-thunk": "registry:dt/redux-thunk#2.1.0+20160703120921"
  }
}

OPPDATERING

Siden det klaget på at count manglet, prøvde jeg å oppdatere til

render(
    <Provider store={store}>
        <ConnectedHelloWorld count={0}/>
    </Provider>,
    container
);

Dette løser problemet. Så problemet er at kompilatoren ikke vet at Provider leverer telleren. Provider bruker butikken. Butikken skal ha verdien clickCount som kartlegges til count av containerkomponenten.

Jeg la merke til at jeg hadde glemt en initialtilstand for butikken. Så selv om typene hadde stemt, ville tilstanden vært tom. Jeg oppdaterte den til

// Store
let initialState:AppState = {clickCount: 0};
let store = createStore(rootReducer, initialState);

Nå er jeg sikker på at clickCount er riktig satt i butikken. Så jeg vil forvente at mapStateToProps-funksjonen tar AppState og returnerer HelloWorldProps som spesifisert, og deretter bør Provider gi tellerverdien. Dette er sant, men kompilatoren ser det ikke.

Så hvordan kan man rette på det?

Svar #1

I mitt tilfelle passerte jeg null for mapDispatchToProps-parameteren i connect-funksjonen på denne måten siden jeg ikke brukte dispatch for denne komponenten:

export default connect(mapStateToProps, null)(MainLayout);

Å endre det til bare å utelate mapDispatchToProps-parameteren løste det for meg

export default connect(mapStateToProps)(MainLayout);

Svar #2

Slik gjør jeg det i en Typescript Redux-app (justert til koden din, men ikke testet)

redigert med kommentar nedenfor

  1. skriv connect med props for den tilkoblede komponenten (ConnectedHelloWorldProps)

    const ConnectedHelloWorld:React.ComponentClass<ConnectedHelloWorldProps>  = 
        connect<any,any,HelloWorldProps>(mapStateToProps)(HelloWorld)
    
    interface ConnectedHelloWorldProps { }
    
    interface HelloWorldProps extends ConnectedHelloWorldProps {
        count: number
        ....
    }
    
  2. bruk den tilknyttede komponenten og dens ConnectedHelloWorldProps props i Provider

    <Provider store={store}>
        <ConnectedHelloWorld/>
    </Provider>
    

Merk: dette fungerer fint med disse typene

"@types/react": "^0.14.52",
"@types/react-dom": "^0.14.19",
"@types/react-redux": "^4.4.35",
"@types/redux-thunk": "^2.1.32",

ConnectedHellowWorldProps er egentlig ikke nødvendig her, siden det er et tomt grensesnitt, men i en reell situasjon vil det sannsynligvis inneholde noen egenskaper.

Det grunnleggende prinsippet er dette: ConnectedHelloWorldProps inneholder det som må sendes på Providernivå. I mapStateToProps og/eller mapDispatchToProps, berik den faktiske komponenten HelloWorldProps med det som trengs

Redux Typescript-typingene er et mareritt, men det som vises ovenfor burde være tilstrekkelig.

export declare function connect<TStateProps, TDispatchProps, TOwnProps>(
mapStateToProps: FuncOrSelf<MapStateToProps<TStateProps, TOwnProps>>,
 mapDispatchToProps?: FuncOrSelf<MapDispatchToPropsFunction<TDispatchProps, TOwnProps> | MapDispatchToPropsObject>): ComponentDecorator<TStateProps & TDispatchProps, TOwnProps>;


 interface ComponentDecorator<TOriginalProps, TOwnProps> {
    (component: ComponentClass<TOriginalProps> | StatelessComponent<TOriginalProps>): ComponentClass<TOwnProps>;
 }

Relaterte artikler