rootReducer.js

import { combineReducers } from "redux";
import counterReducer from "./counter.reducer";

const rootReducer = combineReducers({
    counter: counterReducer
})

export default rootReducer

counterReducer.js

import { DECREMENT_COUNTER, INCREMENT_COUNTER } from "../Constants";
import initialState from "../initialState";

const counterReducer = (state = initialState, action) => {
    switch (action.type) {
        case INCREMENT_COUNTER: {
            return {
                ...state,
                counter: state.counter + 1
            }
        }
        
        case DECREMENT_COUNTER:
            return {
                ...state,
                counter: state.counter - 1
            }

        default:
            return state
    }
}

export default counterReducer

App.js

import React from 'react';
import './App.css';
import { useDispatch, useSelector } from 'react-redux';
import { decrementCounter, incrementCounter } from './actions/counter.action';

function App() {
  const selector = useSelector(state => state)
  console.log(selector)
  const dispatch = useDispatch()
  return (
    <div>
      <h1>Counter: {selector.counter}</h1>
      <button onClick={() => dispatch(incrementCounter())}>Increment</button>
      <button onClick={() => dispatch(decrementCounter())}>Decrement</button>
    </div>
  );
}

export default App;

Index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { Provider } from 'react-redux';
import store from './store';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <Provider store={store}>
    <App />
  </Provider>
);

reportWebVitals();

Store.js

import { configureStore } from "@reduxjs/toolkit";
import initialState from "./initialState";
import rootReducer from "./reducers/root.reducer";

const store = configureStore({
    reducer: rootReducer,
    preloadedState: initialState
})

export default store

InitialState.js

export const initialState = {
    counter: 0
}

因此,我试图实现计数器与增量和减量按钮,但当我点击增量或减量按钮时,它会出现错误,

对象作为React子级无效(找到:具有键的对象 {counter}).

我试过控制台记录状态,发现最初我的状态看起来像

counter: 0

但当我点击递增或递减按钮时,状态变为

counter: {
  counter: NaN
}

我搞不懂为什么会发生这种事.

推荐答案

Issue

如果状态最初是counter: 0,那么我怀疑counterReducer中的initialState实际上就是const initialState = 0;.问题似乎是你的counterReducer函数注入了嵌套的counter属性.

state.counter是未定义的,当你加/减它的结果是NaN,然后所有对NaN的进一步数学运算都是NaN.

const state = 0;

console.log({ state, "state.counter + 1": state.counter + 1 });

一旦state.counter被嵌套属性变异,它就变成了state.counter.counter.就是这个counter.counter对象不能呈现为JSX.

Solution

更新counterReducer个递增/递减情况,以增加或减少仅为计数器值的state.

const counterReducer = (state = initialState, action) => {
  switch (action.type) {
    case INCREMENT_COUNTER:
      return state + 1;
        
    case DECREMENT_COUNTER:
      return state - 1;

    default:
      return state;
  }
}

在UI中,您需要 Select 您想要订阅的特定状态.101写 Select 器函数返回整个状态,例如,不使用useSelector(state => state);.

function App() {
  const counter = useSelector(state => state.counter);
  const dispatch = useDispatch();

  return (
    <div>
      <h1>Counter: {counter}</h1>
      <button onClick={() => dispatch(incrementCounter())}>
        Increment
      </button>
      <button onClick={() => dispatch(decrementCounter())}>
        Decrement
      </button>
    </div>
  );
}

Javascript相关问答推荐

MongoDB中的引用

React 17与React 18中的不同setState行为

Angular material 拖放堆叠的牌副,悬停时自动展开&

如何使用JavaScript将文本插入空div

函数返回与输入对象具有相同键的对象

使用Java脚本根据按下的按钮更改S文本

CheckBox作为Vue3中的一个组件

可更改语言的搜索栏

无法使用单击按钮时的useState将数据从一个页面传递到另一个页面

IF语句的计算结果与实际情况相反

第二次更新文本输入字段后,Reaction崩溃

使用auth.js保护API路由的Next.JS,FETCH()不起作用

将异步回调转换为异步生成器模式

使用父标签中的Find函数查找元素

Docent.cloneNode(TRUE)不克隆用户输入

是否可以将异步调用与useState(UnctionName)一起使用

在Vercel中部署Next.js项目时获取`ReferenceError:未定义文档`

JAVASCRIPT SWITCH CASE语句:当表达式为';ALL';

如何从嵌套的json中获取最大值

使用JavaScript或PHP从div ID值创建锚标记和链接