#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
.
Это основано на предположении, что
- Ваши редукторы вложены, а не в корневом хранилище.
- Вы определили
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.