W.P. McNeill
W.P. McNeill

Reputation: 17026

Typescript error in React Redux connect function

I am trying to write a React Redux component using Typescript following the documentation in Redux Getting Started and Proper Typing of react-redux Connected Components and am getting a type error on my connect function.

My app uses Redux to maintain a table of questions.

state.tsx

import {AnyAction, combineReducers} from "redux";

const ADD_QUESTION = "ADD_QUESTION";

export interface Question {
    id: number,
    text: string
}

export interface State {
    questions: QuestionsTable
}

interface QuestionsTable {
    nextId: number,
    questions: Question[]
}

const questions = (state: State = {questions: {nextId: 0, questions: []}}, action: AnyAction): State => {
    const questions = state.questions;
    switch (action.type) {
        case ADD_QUESTION:
        return {
            ...state,
            questions: {
                ...questions,
                nextId: questions.nextId + 1,
                questions: questions.questions.concat([{id: questions.nextId, text: action.text}])
            }
        };
        default:
            return questions
    }
};

export const reducers = combineReducers({
    questions
});

A Questions component displays them. I use connect to create QuestionsContainer from that.

questions.tsx

import React from "react"
import {Question, State} from "../state";
import {connect} from "react-redux";

export type QuestionsProps = {
    questions: Question[]
}

const Questions: React.FC<QuestionsProps> = ({questions}) => {
    return (
        <div>
            <ul>
                {questions.map(question => (
                    <li>{question.text}</li>
                ))}
            </ul>
        </div>
    )
};

const mapStateToProps = (state: QuestionsTable): QuestionsProps => {
    return {questions: state.questions.questions};
};

export const QuestionsContainer = connect<QuestionsProps>(mapStateToProps)(Questions);

My top-level app displays this container component.

App.tsx

import React from "react";
import "./App.css";
import {reducers} from "./state";
import {createStore} from "redux"
import {Provider} from "react-redux"
import {QuestionsContainer} from "./components/questions";

const store = createStore(reducers);
const App: React.FC = () => {
    return (
        <Provider store={store}>
            <div className="App">
                <QuestionsContainer/>
            </div>
        </Provider>
    );
};

export default App;

I get a type error in my call to connect.

Error:(61, 59) TS2769: No overload matches this call.
  The last overload gave the following error.
    Argument of type '(state: State) => QuestionsProps' is not assignable to parameter of type 'MapStateToPropsParam<QuestionsProps, {}, {}>'.
      Type '(state: State) => QuestionsProps' is not assignable to type 'MapStateToPropsFactory<QuestionsProps, {}, {}>'.
        Types of parameters 'state' and 'initialState' are incompatible.
          Property 'questions' is missing in type '{}' but required in type 'State'.

If I suppress this error with @ts-ignore and log the value of questions passed to my Questions component I see this.

{"nextId":0,"questions":[]}

I can't figure out why the nextId field is there even though mapStateToProps dereferences state.questions.questions.

What is the correct way to set this up?

Upvotes: 0

Views: 243

Answers (1)

Vladislav Khazipov
Vladislav Khazipov

Reputation: 333

Okay I will try it from phone Sorry for formatting

import {AnyAction, combineReducers} from "redux";

const ADD_QUESTION = "ADD_QUESTION";

export interface Question {
    id: number,
    text: string
}

interface State {
    nextId: number,
    items: Question[]
}

const initialState: State = {nextId: 0,  items: []}

const questions = (state = initialState, action: AnyAction): State => {
    switch (action.type) {
        case ADD_QUESTION:
        return {
            ...state,
            items: action.items,
            nextId: action.nextId
        };
        default:
            return state
    }
};

export const reducers = combineReducers({
    questions
});

That's how I see your reducer

Then in component in mapStateToProps questions: state.questions.items

Upvotes: 1

Related Questions