React Redux: необработанное нарушение инварианта (объекты недействительны в качестве дочерних элементов React)

Я получаю сообщение об ошибке: «react-dom.development.js: 55 Uncaught Invariant Violation: объекты недействительны в качестве дочернего элемента React (найдено: объект с ключами {counter}). Если вы хотели отобразить коллекцию дочерних элементов, вместо этого используйте массив».

Это происходит, когда я меняю Counter.js с <p>{this.state.counter}</p> на <p>{this.props.counter}</p>

Насколько я понимаю, я использую mapStateToProps и mapDispatchToProps, и я должен иметь возможность получить хотя бы начальное состояние счетчика, равное 0.

Я не уверен, является ли это причиной проблемы, но я использую console.info для просмотра того, как выглядит состояние, но не уверен, что это правильно:

{counter: {…}}
  counter: {counter: 0}
  __proto__: Object

Counter.js

// Imports: Dependencies
import React, { Component } from 'react';
import { connect } from 'react-redux';

// Imports: Action Types
import { INCREASE_COUNTER, DECREASE_COUNTER } from '../actionTypes/index';

// Component: Counter
class Counter extends React.Component {
  constructor(props) {
    super(props);

  this.state = {
    counter: 0,
  };
}

render() {
  return (
    <div>
      <h2>React Redux Counter</h2>
      <button type = "button" onClick = {() => this.props.increaseCounter()}>Increase</button>
      <p>{this.props.counter}</p>
      <button type = "button" onClick = {() => this.props.decreaseCounter()}>Decrease</button>
    </div>
  );
 }
}

// Map State To Props (Reducers)
const mapStateToProps = (state) => {
  console.info('State:');
  console.info(state);
  console.info('');

  return {
    counter: state.counter,
  };
};

// Map Dispatch To Props (Actions)
const mapDispatchToProps = (dispatch) => {
  return {
    increaseCounter: () => dispatch({ type: INCREASE_COUNTER }),
    decreaseCounter: () => dispatch({ type: DECREASE_COUNTER }),
  };
};

// Exports
export default connect(mapStateToProps, mapDispatchToProps)(Counter);

App.js

// Imports: Dependencies
import React, { Component } from 'react';

// Imports: Components
import Counter from './components/Counter';

// React Application
class App extends Component {
  render() {
    return (
      <div>
        <Counter />
      </div>
    );
  }
}

// Exports
export default App;

index.js

// Imports: Dependencies
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './store/store';

// Imports: React Application
import App from './App';

// Render App
ReactDOM.render(
  <Provider store = {store}>
    <App />
  </Provider>,
  document.getElementById('app'),
);

store.js

// Imports: Dependencies
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { createLogger } from 'redux-logger';

// Imports: Redux
import rootReducer from '../reducers/index';

// Redux: Thunk (Async/Await)
const middleware = [thunk];
if (process.env.NODE_ENV !== 'production') {
  middleware.push(createLogger());
}

// Redux: Store
const store = createStore(
  rootReducer,
  applyMiddleware(...middleware),
);

// Exports
export default store;

counterReducer.js

import { INCREASE_COUNTER, DECREASE_COUNTER } from '../actionTypes/actionTypes';

// Initial State
const initialState = {
  counter: 0,
};

// Redux: Counter Reducer
const counterReducer = (state = initialState, action) => {
  switch (action.type) {
    case INCREASE_COUNTER: {
      return {
        ...state,
        counter: state.counter + 1,
      };
    }
    case DECREASE_COUNTER: {
      return {
        ...state,
        counter: state.counter - 1,
      };
    }
    default: {
      return state;
    }
  }
};

// Exports
export default counterReducer;

actionTypes.js

export const INCREASE_COUNTER = 'INCREASE_COUNTER';

export const DECREASE_COUNTER = 'DECREASE_COUNTER';
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
0
0
806
1

Ответы 1

Ваше состояние устроено следующим образом:

{
    counter: {
        counter : 0
    }
}

Он структурирован таким образом, потому что ваш counterReducer определяет вложенное поле с именем counter, а затем counterReducer объединяется в более крупный объект, потому что он передается combineReducers({counter : counterReducer}).

В вашем компоненте вы визуализируете:

<p>{this.props.counter}</p>

Но props.counter будет объектом, как {counter : 0}.

React не позволит вам просто вставить объект в вывод рендеринга — он не знает, что с ним делать.

Если вы просто хотите показать значение счетчика, то оно должно быть:

<p>{this.props.counter.counter}</p>

Другим вариантом было бы изменить ваш mapStateToProps на:

const mapStateToProps = (state) => {
  return {
    counter: state.counter.counter,
  };
};

Третий вариант — изменить counterReducer так, чтобы он отслеживал только само число как аргумент state редуктора, а не вкладывал это значение в объект.

Я знал, что склоняюсь в правильном направлении, но не был уверен. Спасибо за помощь и разъяснения!

Jeff Lewis 26.02.2019 23:28

Другие вопросы по теме