2017-12-04 3 views
1

Ich habe Mühe, meine Aktionen in verbundenen Komponenten typsicher zu halten.Wie können Aktionsersteller in verbundenen Komponenten mit Redux, React und TypeScript verwaltet werden?

Grundsätzlich, wenn ich ein paar Redux Aktion Schöpfer importieren, wickeln Sie sie mit dem Dispatcher react-redux und geben sie als Requisiten zu einer Komponente verwendet wird, würde Ich mag die daraus resultierenden Aktionen der ursprünglichen Typinformationen aus den importierten Funktionen aufrecht zu erhalten.

Aktionen haben Typen und Rückgabetyp abgeleitet wird:

export const actionA = (p1: string, p2: number) => 
    ({ type: 'EXAMPLE_A', payload: { p1, p2 } }) 

export const actionB = (p1: number) => 
    ({ type: 'EXAMPLE_B', payload: p1 }) 

Aber meine Komponente noch einige any Typen haben den Compiler zu erfüllen, gibt Sicherheit zu verlieren.

import * as React from 'react' 
import { Dispatch, bindActionCreators } from 'redux' 
import { connect } from 'react-redux' 
import * as exampleActions from '../actions/example' 

interface MyComponentProps { 
    someStore: SomeStoreState, 
    actions: any // <-- Just use whatever types exampleActions have? 
} 

class MyComponent extends React.Component<MyComponentProps, {}> { 

    constructor(props: MyComponentProps) { 
    super(props) 
    } 

    private foo() { 
    const { actionA } = this.props.actions.exampleActions 
    actionA('foo', 'bar') // <-- Compile time error pls 
    } 


    public render() { 
    return null 
    } 

} 

const mapStateToProps = (state: RootState) => ({ 
    someStore: state.someStore 
}) 

const mapDispatchToProps = (dispatch: Dispatch<any>) => ({ 
    actions: { 
    exampleActions: bindActionCreators<any>(exampleActions, dispatch) 
    } 
}) 

export default connect (mapStateToProps, mapDispatchToProps)(MyComponent) 

Deklaration der Funktion Parametertypen wieder in den Requisiten Schnittstelle sorta hilft, aber ich würde nur die ursprünglichen Arten erhalten möchten, so dass sie an einem Ort definiert sind.

Ich interessiere mich nicht wirklich für die Typen innerhalb des Dispatcher selbst, also irgendwie die exampleTypes (und irgendwelche anderen Aktionen ') Art Informationen zu den Stützen gießen würde eine gute Lösung für mich sein, als ob die Versandbindung warn Da waren sie überhaupt und die Schöpfer selbst wurden als Requisiten verabschiedet.

Zusätzlich verwendet die Anwendung redux-promise-middleware, was bedeutet, dass einige Aktionen Versprechungen zurückgeben können. Ich möchte auch, dass diese Informationen erhalten bleiben, damit Aktionen innerhalb der Komponente verkettet werden können. Aber ich denke, mit Casting sollte das kein Problem sein.

+1

Überprüfen Sie diese Bibliothek https://github.com/aikoven/typescript-fsa – MistyK

Antwort

1

Sie müssen Ihre Aktionsersteller explizit eingeben und dann ihre Typen zusammen mit den Funktionen selbst importieren. Das Erstellen einiger generischer Aktionsschnittstellen kann dabei helfen, da die Reduxtypen normalerweise nicht hilfreich sind. Es ist ein wenig wortreich, aber die Art der Unterstützung ist es oft wert, vor allem, weil Sie ausgezeichnete Typings auch in Ihren Reduzierern bekommen können.

ich verwenden in der Regel so etwas wie dies für die Aktionen/Schöpfer:

export interface TypedAction<TAction, TPayload> { 
    type: TAction; 
    payload: TPayload; 
} 

export type TypeA = "EXAMPLE_A"; 
export type TypeB = "EXAMPLE_B"; 

export interface PayloadA { 
    p1: string; 
    p2: number; 
} 

export interface PayloadB { 
    p1: number; 
} 

export type ActionA = TypedAction<TypeA, PayloadA>; 
export type ActionB = TypedAction<TypeB, PayloadB>; 

export type Actions = ActionA | ActionB; 

export type ActionCreatorA = (p1: string, p2: number) => ActionA; 

export type ActionCreatorB = (p1: number) => ActionB; 

export const actionCreatorA: ActionCreatorA = (p1, p2) => ({ 
    type: "EXAMPLE_A", 
    payload: { 
     p1, 
     p2 
    } 
});  

export const actionCreatorB: ActionCreatorB = (p1) => ({ 
    type: "EXAMPLE_B", 
    payload: { 
     p1 
    } 
}); 

, die in einer Komponente verwendet werden können, wie:

import * as React from 'react' 
import { Dispatch, bindActionCreators } from 'redux' 
import { connect } from 'react-redux' 
import { 
    actionCreatorA, ActionCreatorA, 
    actionCreatorB, ActionCreatorB 
} from '../actions/example' 

interface MyComponentProps { 
    someStore: SomeStoreState; 
    actionCreatorA: ActionCreatorA; 
    actionCreatorB: ActionCreatorB; 
} 

class MyComponent extends React.Component<MyComponentProps, {}> { 

    constructor(props: MyComponentProps) { 
     super(props) 
    } 

    private foo() { 
     const { actionA } = this.props; 
     actionA('foo', 'bar') // <-- Compiles 
    } 


    public render() { 
     return null 
    } 

} 

const mapStateToProps = (state: RootState) => ({ 
    someStore: state.someStore 
}) 

const mapDispatchToProps = { 
    actionCreatorA 
}; 

export default connect (mapStateToProps, mapDispatchToProps)(MyComponent) 

Reduzierungen können auch profitieren, indem sie mit:

import (Actions } from "./actions/example"; 

// Actions here is the union type of all actions this reducer will 
// handle, as exported from the actions file 
export const someReducer = (state = defaultState, action: Actions) => { 
    switch (action.type) { 
     case "EXAMPLE_A": 
      // action is typed as ActionA 
      return { 
       p1: action.p1, 
       p2: action.p2 
      }; 
     case "EXAMPLE_B": 
      // action is typed as ActionB 
      return { 
       p1: action.p1, 
       p2: action.p2 // <-- Does not compile, p2 does not exist on ActionB 
      }; 
     default: 
      return state; 
    } 
} 
+0

Danke, das ist hilfreich, aber ich w Ich akzeptiere es noch nicht als Antwort, falls es andere Wege gibt. Scheint wie eine erhebliche Menge an Boilerplate, wenn ich im Grunde wollte nur die Parameter Typ Informationen für welche Aktion Schöpfer ich importiert und ich nicht viel Wert auf Typ-Sicherheit in der tatsächlichen Dispatch-Prozess, nur damit die Parameter der verbundenen Aktionen übereinstimmen. Vielleicht gibt es keinen einfacheren Weg damit umzugehen, aber ich werde weiter nach etwas suchen. – Voidsheep

Verwandte Themen