React / Redux: почему я получаю эту ошибку.props в моем контейнере?

#reactjs #redux

#reactjs #redux

Вопрос:

Я не могу понять, почему я получаю эту ошибку «TypeError: this.props.reviews.map не является функцией» для моего файла ReviewsContainer.

Я чувствую, что что-то упускаю, но я полагаю, что я определил свой реквизит в компоненте Reviews .

Здесь у меня есть компонент отзывов:

 /components/reviews/Reviews.js


import React from "react";
import { NavLink } from "react-router-dom";

const ReviewCard = props => {
  const url = `/reviews/${props.review.id}`;
  return (
    <div className="ReviewCard">
      <NavLink to={url} exact>
        <p>
          <strong>Title:</strong> {props.review.title}
        </p>
      </NavLink>

      <p>
        <strong>Content: </strong>
        {props.review.content}
      </p>
      <img className="ReviewImage" src={props.review.img_url} alt="(no pic)" />

    </div>
  );
};

export default ReviewCard;
  

Мой файл действия выборки:

 /actions/reviewsFetch.js

export const reviewsFetch = () => {

    return dispatch => {
        fetch('http://localhost:3000/api/v1/reviews', {
            method: "GET",
            headers: {
                "Content-Type": "application/json"
            }
        })
        .then(response => response.json())
        .then(reviews => dispatch ({
            type: 'REVIEWS_FETCH',
            payload: reviews.data
        }))
    }
}
  

и, наконец, мой файл ReviewsContainer:

 /containers/ReviewsContainer.js

import React, { Component } from "react";
import { connect } from "react-redux";
import { reviewsFetch } from "../actions/reviewsFetch";
import ReviewCard from "../components/reviews/Reviews";


class ReviewsContainer extends Component {

  componentDidMount() {
    this.props.reviewsFetch();
  }
  render() {
    return (
      <div>
        {this.props.reviews.map((review, index) => (
          <ReviewCard review={review} key={index} />
        ))}
      </div>
    );
  }
}

const mapStateToProps = state => {
  return {
    reviews: state.reviews
  };
};

export default connect(
  mapStateToProps,
  { reviewsFetch }
)(ReviewsContainer);
  

Отзывы Редуктор

 export default function reviewReducer (state = {reviews: [], comments: []}, action) {
    switch (action.type) {
        case 'REVIEWS_FETCH':
            return {...state, reviews: action.payload}
        default:
            return state
    }
}

  

index.js

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

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

store.js

 import { createStore, applyMiddleware, compose, combineReducers } from 'redux'
import thunk from 'redux-thunk'
import reviews from './reducers/reviewReducer'

const reducers = combineReducers({
    reviews
  })
  
  const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
  const store = createStore(reducers, composeEnhancer(applyMiddleware(thunk)))

  export default store
  

Комментарии:

1.Эта ошибка сообщает вам, что reviews определено, но не является массивом. Пожалуйста, включите ваш редуктор, чтобы мы могли видеть, как reviews он настраивается.

2. @BrianThompson Я добавил редуктор в конце

3. Хорошо, где вы настраиваете свой магазин? Это влияет на вложенную структуру. Поскольку state.reviews не определено (и, похоже, является объектом), я предполагаю, что ваше хранилище настроек называет редуктор reviews , означающий, что вам нужно будет получить к нему доступ как state.reviews.reviews .

4. Он настроен в моем index.js файл, я думаю (я очень новичок в этом). Я также добавил свой индекс и хранилище в конце.

Ответ №1:

Здесь есть несколько источников путаницы, но простой ответ — изменить mapStateToProps функцию следующим образом:

 const mapStateToProps = state => {
  return {
    // reviews: state.reviews
    reviews: state.reviews.reviews
  };
};
  

Вот почему

Состояние — это объект. Этот объект может соответствовать одному редуктору или может быть вложенным для включения других объектов, массивов и т. Д. Эти вложенные объекты обычно соответствуют другим функциям редуктора. Все эти редукторы объединяются в один объект состояния by combineReducers .

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

 import reviews from './reducers/reviewReducer'

const reducers = combineReducers({
  reviews
})
  

Оператор import важен, потому что, несмотря на то, что ваш редуктор назван reviewReducer , он экспортируется по умолчанию и импортируется как reviews . Это в дополнение к combineReducers вызову функции (в отличие от простого использования одного редуктора) приведет к тому, что объект состояния будет выглядеть следующим образом:

 state: {
  reviews: {
    reviews: [], 
    comments: []
  }
}
  

Это означает, что для доступа к массиву, который вы хотите, вам нужно использовать state.reviews.reviews (@heshiebee был на правильном пути). Это именование зависит от вас. Например, вы можете изменить его следующим образом:

 const reducers = combineReducers({
  myNestedState: reviews
})
  

Тогда вы получите к нему доступ state.myNestedState.reviews следующим образом.

Комментарии:

1. потрясающе, большое вам спасибо! такое хорошее объяснение, я действительно ценю это

Ответ №2:

Похоже, вам не хватает имени редуктора Redux.

Итак, каким бы ни было ваше имя редуктора, вы бы сделали state.[reducer name].reviews .

Это основано на предположении, что

  1. Ваши редукторы вложены, а не в корневом хранилище.
  2. Вы определили reviews как пустой массив в своем редукторе.

Комментарии:

1. Это возможно, но также возможно, что у них есть только один редуктор и state это один неназначенный объект.

2. Итак, я добавил state.reviewReducer.reviews, и теперь я получаю сообщение об ошибке, в котором говорится, что невозможно прочитать свойство ‘reviews’ undefined, а reviews — это пустой массив в моем редукторе (я добавил свой файл reducer в конце, выше)

3. Вы импортировали свой редуктор в index.js ? разместите свой index.js код.

Ответ №3:

Во время монтирования ваш ReviewsContainer компонент this.props.reviews , возможно, не был заполнен как массив, попробуйте сначала добавить проверку, чтобы убедиться, что this.props.reviews он уже заполнен как массив, прежде чем пытаться отобразить его следующим образом

 {this.props.reviews.length amp;amp; this.props.reviews.map((review, index) => (
  

Комментарии:

1. Это не поможет, поскольку ошибка говорит map , что это не функция. Если reviews не было определено (falsy), было бы сказано «невозможно получить доступ к карте свойств undefined» или «null» и т. Д.

2. @Брайан Томпсон обновил мой ответ.

3. С обновлением этого было бы достаточно, чтобы предотвратить ошибку, но здесь почти наверняка есть другая ошибка, которая является настоящей проблемой. Если тип reviews данных меняется (похоже, от объекта к массиву), это либо не намеренно, либо действительно плохая практика. Это действительно проблема, которую должен решить OP.