React + Redux: компонент не обновляется
Попробуйте React + Redux, и, возможно, я делаю что-то явно глупое, потому что компонент, который запускает действие для извлечения данных по сети, не обновляется (перерисовывается), когда данные извлекаются.
Вот соответствующие биты моего кода:
Index.js верхнего уровня, служащий точкой входа для приложения:
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import { Router, browserHistory } from 'react-router';
import reduxPromise from 'redux-promise';
import createLogger from 'redux-logger';
const logger = createLogger();
import routes from './routes';
import reducers from './reducers';
const createStoreWithMiddleware = applyMiddleware(reduxPromise, logger)(createStore);
ReactDOM.render(
<Provider store={createStoreWithMiddleware(reducers)}>
<Router history={browserHistory} routes={routes} />
</Provider>
, document.querySelector('.container'));
Контейнер верхнего уровня
import React, {Component} from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as Actions from '../actions';
import Header from '../components/header';
import Showcase from '../components/showcase';
function mapStateToProps(state) {
return {
resources: state.resources
}
}
function mapDispatchToProps(dispatch) {
return {
fetchResources: () => {
dispatch(Actions.fetchResources());
}
}
}
class App extends Component {
render() {
console.log('props in App', this.props);
return (
<div>
<Header/>
<Showcase
fetchResources={this.props.fetchResources}
resources={this.props.resources}
/>
</div>
);
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(App)
Компонент, который запускает действие для отправки запроса данных, когда он собирается смонтировать, и должен показывать извлеченные данные:
import React, {Component} from 'react';
import {connect} from 'react-redux';
class Showcase extends Component {
constructor(props) {
super(props);
}
componentWillMount() {
this.props.fetchResources();
}
render() {
console.log('resources', this.props);
return (
<div>
This is showcase
</div>
);
}
}
export default connect(state => ({resources: state.resources}))(Showcase)
Создатель действий:
import * as types from '../constants/ActionTypes';
import axios from 'axios';
export function fetchResources() {
return {
type: types.FETCH_FIRST,
payload: axios.get('/sampledata/1.json')
}
}
Редуктор для действия при извлечении:
import * as types from '../constants/ActionTypes';
export default function resourcesReducer (state={}, action) {
switch (action.type) {
case types.FETCH_FIRST:
console.log('about to return', Object.assign (state, {resources: action.payload.data }))
return Object.assign (state, {resources: action.payload.data });
default:
return state
}
};
и, наконец, корневой редуктор:
import { combineReducers } from 'redux';
import navigationReducer from './navigation-reducer';
import resourcesReducer from './resources-reducer';
const rootReducer = combineReducers({
navigationReducer,
resourcesReducer
});
export default rootReducer;
Итак, вот что я наблюдаю. Действие для запроса данных успешно инициировано, запрос отправляется, редуктор получает его, когда обещание разрешается, и обновляет состояние с выбранными данными. На данный момент, я бы ожидал на высшем уровнеApp
компонент иShowcase
компонент для обнаружения того, что магазин обновился, и для повторного рендеринга, но я не вижу его в консоли.
Кроме того, я смущенredux-logger
Консольный вывод:
В частности, я удивлен, увидев, чтоstate
содержит редукторы от rootReducer - я не знаю, правильно ли это (пример наRedux logger Github page показывает состояние без редукторов). Также кажется удивительным, чтоprev state
как сообщаетсяredux-logger
содержит то же самоеresourcesReducer
объект какnext state
хотя интуитивно я бы ожидалprev state
быть более или менее пустым.
Не могли бы вы указать, что я делаю неправильно и как заставить компоненты React реагировать наstate
изменения?
==================================================
ОБНОВЛЕНО:
1) поменялmapStateToProps
функция вApp
компонент, так что он правильно отображается в состояния редуктора:
function mapStateToProps(state) {
return {
resources: state.resourcesReducer
}
}
2) Все еще прохождениеresources
вплоть до `Showcase компонента:
render() {
console.log('props in App', this.props);
return (
<div>
<Header navigateActions={this.props.navigateActions}/>
React simple starter
<Showcase
fetchResources={this.props.fetchResources}
resources={this.props.resources}
/>
</div>
);
3) Попытка отобразитьresources
на экране, чтобы увидеть, что на самом деле внутри этого объекта:
render() {
console.log('resources', this.props);
return (
<div>
This is showcase {JSON.stringify(this.props.resources)}
</div>
);
}
Смотрите это на экране:This is showcase {}
, Компонент не рендерится.
Вот снимок экрана консоли, показывающий, что реквизиты приложения обновлены значениями изnext state
, Тем не менее, это не привело к повторной визуализации компонента:
ОБНОВЛЕНО СНОВА: И мой javascript-фу тоже был беден. Я не совсем понял, что, возвращаясьObject.assign (state, {resources: action.payload.data });
Я фактически мутировал в этом состоянии, и что простая инверсия аргументов позволила бы мне достичь того, что я хотел. Благодаряэто обсуждение на ТАК для просветления.