Redux useSelector возвращает объект с ключом ‘0’ и значением состояния вместо обычного состояния

#reactjs #typescript #object #redux #react-hooks

#reactjs #typescript #объект #сокращение #реагирующие хуки

Вопрос:

Я создал простое приложение Redux со следующими частями

 import { useDispatch, useSelector } from "react-redux";
import { buyCakes } from "../redux/cake/cake.actions";
import { ICakeState } from "../redux/cake/cake.reducer";
import { changeEventType, formEventType } from "../models/events.model";

const CakeContainer: React.FC = () => {
  const [buyCakeAmount, setBuyCakeAmount] = useState(1);

  const numberOfCakes = useSelector<ICakeState, ICakeState["numberOfCakes"]>(
    (state) => {
      console.log(state);
      return state.numberOfCakes;
    }
  );

  const dispatch = useDispatch();

  const changeHandler = (event: changeEventType) => {
    setBuyCakeAmount(parseInt(event.target.value));
  };

  const submitHandler = (event: formEventType) => {
    event.preventDefault();
    dispatch(buyCakes(buyCakeAmount));
    setBuyCakeAmount(1);
  };

  return (
    <>
      <h2>Number of cakes left in the shop: {numberOfCakes}</h2>
      <form onSubmit={submitHandler}>
        <input type="number" value={buyCakeAmount} onChange={changeHandler} />
        <button type="submit">Buy Cakes</button>
      </form>
    </>
  );
};

export default CakeContainer;
 

Редуктор

 import { BUY_CAKE, BUY_CAKES } from "./cake.types";
import { CakeActionTypes } from "./cake.types";

export interface ICakeState {
  numberOfCakes: number;
}

const INITIAL_STATE: ICakeState = {
  numberOfCakes: 10,
};

const cakeReducer = (
  state: ICakeState = INITIAL_STATE,
  action: CakeActionTypes
) => {
  switch (action.type) {
    case BUY_CAKE:
      return {
        ...state,
        numberOfCakes: state.numberOfCakes - 1,
      };

    case BUY_CAKES:
      return {
        ...state,
        numberOfCakes: state.numberOfCakes - action.payload,
      };

    default:
      return state;
  }
};

export default cakeReducer;
 

Действия

 import { BUY_CAKE, BUY_CAKES } from "./cake.types";
import { CakeActionTypes } from "./cake.types";
import { numberOfCakes } from "../../models/cake.model";

export const buyCake = (): CakeActionTypes => ({
  type: BUY_CAKE,
});

export const buyCakes = (numberOfCakes: numberOfCakes): CakeActionTypes => ({
  type: BUY_CAKES,
  payload: numberOfCakes,
});
 

Типы

 import { numberOfCakes } from "../../models/cake.model";

export const BUY_CAKE = "BUY_CAKE";
export const BUY_CAKES = "BUY_CAKES";

interface BuyCakeAction {
  type: typeof BUY_CAKE;
}

interface BuyCakesAction {
  type: typeof BUY_CAKES;
  payload: numberOfCakes;
}

export type CakeActionTypes = BuyCakeAction | BuyCakesAction;
 

Магазин

 import { combineReducers, createStore } from "redux";
import { composeWithDevTools } from "redux-devtools-extension";
import cakeReducer from "./cake/cake.reducer";

export default createStore(
  combineReducers([cakeReducer]),
  composeWithDevTools()
);
 

Журнал в useSelector регистрирует этот объект

 {"0":{"numberOfCakes":10}}
 

Разве он не должен просто возвращать состояние, не помещая его в объект с ключом ‘0’?
Кроме того, если я просто верну состояние следующим образом

 state['0'].numberOfCakes
 

Я получаю сообщение об ошибке машинописного текста
Может кто-нибудь, пожалуйста, объяснить мне, почему это происходит и как это исправить, спасибо.

Ответ №1:

Первая проблема: useSelector собирается вернуть вам ваше корневое состояние, поэтому вы передаете неправильные типы в свой общий здесь:

 const numberOfCakes = useSelector<ICakeState, ICakeState["numberOfCakes"]>(
  (state) => {
    console.log(state);
    return state.numberOfCakes;
  }
);
 

Вторая проблема заключается в том, что combineReducers принимает объект с различными редукторами, включая их имена, а не массив, поэтому вам нужно изменить его на:

 export default createStore(
  combineReducers({cakeReducer}),
  composeWithDevTools()
);

export type RootState = ReturnType<typeof rootReducer>
 

а затем измените:

 const numberOfCakes = useSelector<RootState>(
  (state) => {
    console.log(state);
    return state.cakeReducer.numberOfCakes;
  }
);