Sai
Sai

Reputation: 841

How to use props from connect without having to explicitly write them when rendering component?

I am using redux/connect to get state and actions creators as props in my component. This, however, causes an error with Typescript when rendering the component as the props i'm receiving via connect are not explicitly written.

Is there a way to get around this without making the types optional or is there something else I am missing?

//FetchData Component

import React, {Component} from 'react';
import { connect } from "react-redux";
import { 
    getData, 
    filterMaterial,
    filterSize,
    resetMap
} from "../js/actions/index";

import { MapProperties } from '../types/state';
import { Appstate } from '../js/store';

type DispatchProps = {
    getData: () => void, 
    filterMaterial: (name:string) => void,
    filterSize: (name:string) => void,
    resetMap: () => void
}

type Props = DispatchProps & LinkStateProps

class FetchData extends Component<Props> {
    constructor(props: Props) {
        super(props);
        
        this.handleReset = this.handleReset.bind(this)
    }

    handleReset = () => {
        if(this.props.resetMap) this.props.resetMap();
    }
    
    componentDidMount() {
        if (this.props.getData) this.props.getData()
    }

    render() {        
        return (
         ...
        );
    }
}

type LinkStateProps = {
    geoJSON: MapProperties,
    mapJSON: MapProperties
}

const mapStateToProps = (state: Appstate, props:Props):LinkStateProps => {
    return { 
        geoJSON: state.geoJSON,
        mapJSON: state.mapJSON
    };
};

export default connect(mapStateToProps, {
    getData, 
    filterMaterial,
    filterSize,
    resetMap
})(FetchData);

//App Component

import React from 'react';
import './App.scss';
import FetchData from './components/FetchData';

function App() {
    return (
        <div className="App">
            //error = Type '{}' is missing the following properties from type 'DispatchProps': getData, filterMaterial, filterSize, resetMap
            <FetchData />
        </div>
    );
}

export default App;

Upvotes: 1

Views: 143

Answers (1)

Bill Doughty
Bill Doughty

Reputation: 2310

It's because you're passing Props into your mapToState function.

try:

- const mapStateToProps = (state: Appstate, props:Props):LinkStateProps => {
+ const mapStateToProps = (state: Appstate):LinkStateProps => {
    return { 
        geoJSON: state.geoJSON,
        mapJSON: state.mapJSON
    };
};

The second argument to mapToState is an optional ownProps argument that is only needed if you need any of the component's "own" props (not connected props) to create the proper mapping from state to props. Providing Props as you did makes TypeScript assume that those props must be provided explicitly when using the component, hence the error you were seeing.


Also, you might consider using some built-in features that TypeScript offers to save you from typing things that TypeScript can infer types from automatically.

Consider this:

import React, {Component} from 'react';
import { connect } from "react-redux";

import { 
    getData, 
    filterMaterial,
    filterSize,
    resetMap
} from "../js/actions/index";

import { MapProperties } from '../types/state';
import { Appstate } from '../js/store';

const mapStateToProps = (state: Appstate) => ({
  geoJSON: state.geoJSON,
  mapJSON: state.mapJSON
});

const dispatchProps = {
  getData,
  filterMaterial,
  filterSize,
  resetMap
}

// You could do:

// type LinkStateProps = ReturnType<typeof mapStateToProps>;
// type DispatchProps = typeof dispatchProps;
// type Props = LinkStateProps & DispatchProps;

// or in one line:
type Props = ReturnType<typeof mapStateToProps> & typeof dispatchProps;

class FetchData extends Component<Props> {
    constructor(props: Props) {
        super(props);
        this.handleReset = this.handleReset.bind(this)
    }

    handleReset = () => {
        if(this.props.resetMap) this.props.resetMap();
    }

    componentDidMount() {
        if (this.props.getData) this.props.getData()
    }

    render() {
        return (
         <p>something</p>
        );
    }
}

export default connect(mapStateToProps, dispatchProps)(FetchData);

Creating an object named dispatchProps (which you do anyway to supply to connect) lets you use Typescript's typeof operator to auto-generate the type signature. And since mapStateToProps returns an object, you can use ReturnType<typeof mapStateToProps> to get the type signature of that object. Combining these two with the '&' operator gives you your Props type. It's essentially a more concise (and less error-prone) way to do the exact same thing you were doing.

Upvotes: 2

Related Questions