Мое приложение отображает список видео, нажимая на каждое видео, выбирает и отображает список комментариев, опубликованных для этого видео.
Я пытаюсь настроить свой магазин Redux для создания редуктора с ключом на основеID видео и хранить связанные комментарии в виде массива.Идея состоит в том, чтобы кэшировать эти идентификаторы и при переключении между видео отображать то, что уже было извлечено, прежде чем разбивать на страницы дополнительные результаты по запросу.
Я делаю это в КОММЕНТАРИИ СНИЖЕНИЯ:
const byVideo = (state = {}, action) => {
const comments = (state = [], action) => {
switch (action.type) {
case GET_COMMENTS_SUCCESS:
return action.comments.map(comment => comment._id);
default:
return state;
}
};
switch (action.type) {
case GET_COMMENTS_SUCCESS:
return {
[action.videoId]: comments(state[action.videoId], action)
};
default:
return state;
}
};
У меня проблема с mapStateToProps и моими селекторами.mapStateToProps вычислил данные до того, как сработало мое действие, и данные извлекаются, и в редукторе создается динамический ключ.В результате, когда я пытаюсь отобразить список идентификаторов комментариев, связанных с ключом, я получаю «Невозможно прочитать свойство map» из неопределенного.
Specifically on:
getVisibleComments
reducers/index.js:33
30 | export const getVisibleComments = (state, videoId) => {
31 | const ids = fromComments.getIds(state.comments, videoId);
32 | console.log(ids);
> 33 | return ids.map(id => fromComments.getComment(state.comment,
id));
34 | };
Я попытался последовать примеру ReduxReddit API = https://redux.js.org/advanced/example-reddit-api,, где ключ редуктора также создается динамически с помощью [action.subreddit].
Есть ли альтернативное решение для генерации динамического ключа здесь, или я обречен с моим текущим подходом, потому чтоmapStateToProps всегда будет вычисляться до того, как компонент полностью смонтируется, и моя выборка будет отправлена?
import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
import { connect } from 'react-redux';
import { fetchComments } from '../store/actions/comments';
import { getVisibleComments, getIsCommentsFetching } from
'../store/reducers';
import CommentList from './commentList';
class visibleCommentList extends Component {
componentDidMount() {
const { vimeo_id } = this.props.match.params;
this.props.fetchComments(vimeo_id);
}
render() {
const { isFetching, comments } = this.props;
return <CommentList isFetching={isFetching} data={comments} />;
}
}
const mapStateToProps = (state, ownProps) => {
const vimeo_id = ownProps.match.params.vimeo_id;
return {
comments: getVisibleComments(state, vimeo_id),
isFetching: getIsCommentsFetching(state)
};
};
export default withRouter(
connect(
mapStateToProps,
{ fetchComments }
)(visibleCommentList)
);
// СНИЖЕНИЕ КОММЕНТАРИИ
import { combineReducers } from 'redux';
import {
GET_COMMENTS_SUCCESS,
GET_COMMENTS_REQUEST,
GET_COMMENTS_FAILURE
} from '../actions/types';
const byIds = (state = {}, action) => {
const { comments } = action;
switch (action.type) {
case GET_COMMENTS_SUCCESS:
const nextState = { ...state };
comments.forEach(comment => (nextState[comment._id] = comment));
return nextState;
default:
return state;
}
};
const byVideo = (state = {}, action) => {
const comments = (state = [], action) => {
switch (action.type) {
case GET_COMMENTS_SUCCESS:
return action.comments.map(comment => comment._id);
default:
return state;
}
};
switch (action.type) {
case GET_COMMENTS_SUCCESS:
return {
[action.videoId]: comments(state[action.videoId], action)
};
default:
return state;
}
};
const isFetching = (state = false, action) => {
switch (action.type) {
case GET_COMMENTS_REQUEST:
return true;
case GET_COMMENTS_SUCCESS:
case GET_COMMENTS_FAILURE:
return false;
default:
return state;
}
};
const comments = combineReducers({
byIds,
isFetching,
byVideo
});
export default comments;
export const getComment = (state, id) => state.byIds[id];
export const getIds = (state, videoId) => state.byVideo[videoId];
export const getIsCommentsFetching = state => state.isFetching;
// ROOT REDUCER
import { combineReducers } from 'redux';
import createVideoIdsList, * as fromList from './createVideoIdsList';
import byIds, * as fromById from './byIds';
import comments, * as fromComments from './commentsByIds';
const listByFilter = combineReducers({
all: createVideoIdsList('all'),
plays: createVideoIdsList('plays'),
likes: createVideoIdsList('likes'),
release_date: createVideoIdsList('release_date'),
comments: createVideoIdsList('comments')
});
const rootReducer = combineReducers({
byIds,
listByFilter,
comments
});
export default rootReducer;
export const getVisibleVideos = (state, filter) => {
const ids = fromList.getIds(state.listByFilter[filter]);
return ids.map(id => fromById.getVideo(state.byIds, id));
};
export const getIsFetching = (state, filter) =>
fromList.getIsFetching(state.listByFilter[filter]);
export const getVisibleComments = (state, videoId) => {
const ids = fromComments.getIds(state.comments, videoId);
console.log(ids);
return ids.map(id => fromComments.getComment(state.comment, id));
};
export const getIsCommentsFetching = state =>
fromComments.getIsCommentsFetching(state.comments);