Reaccionar + Redux: el componente no se actualiza
Probar React + Redux, y probablemente estoy haciendo algo obviamente estúpido, porque un componente que dispara una acción para recuperar datos a través de la red no se actualiza (se vuelve a representar) cuando se recuperan los datos.
Aquí están los bits relevantes de mi código:
El index.js de nivel superior sirve como punto de entrada para la aplicación:
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'));
Aplicación de contenedor de nivel superior:
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)
Componente que desencadena una acción para enviar una solicitud de datos cuando está a punto de montar y se supone que muestra los datos recuperados:
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)
Creador de acción:
import * as types from '../constants/ActionTypes';
import axios from 'axios';
export function fetchResources() {
return {
type: types.FETCH_FIRST,
payload: axios.get('/sampledata/1.json')
}
}
Reductor para la acción de búsqueda:
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
}
};
y finalmente el reductor de raíz:
import { combineReducers } from 'redux';
import navigationReducer from './navigation-reducer';
import resourcesReducer from './resources-reducer';
const rootReducer = combineReducers({
navigationReducer,
resourcesReducer
});
export default rootReducer;
Entonces, esto es lo que estoy observando. La acción para solicitar datos se activa con éxito, se envía una solicitud, el reductor la recibe cuando se resuelve la promesa y actualiza el estado con los datos recuperados. En este punto, esperaría el nivel superiorApp
componente y elShowcase
componente para detectar que la tienda se ha actualizado y volver a renderizar, pero no lo veo en la consola.
Además, estoy confundido porredux-logger
Salida de la consola:
Específicamente, me sorprende ver que elstate
contiene reductores del rootReducer: no sé si es correcto (un ejemplo enPágina de Github del registrador de Redux muestra un estado sin reductores). También parece sorprendente que elprev state
según lo informado porredux-logger
contiene lo mismoresourcesReducer
objeto como elnext state
, aunque intuitivamente esperaríaprev state
estar más o menos vacío.
¿Podría indicarme qué estoy haciendo mal y cómo hacer que los componentes React respondan astate
cambios?
==================================================
ACTUALIZADO:
1) Cambió elmapStateToProps
funcionar en elApp
componente para que se asigne correctamente a los estados reductores:
function mapStateToProps(state) {
return {
resources: state.resourcesReducer
}
}
2) Aún pasando elresources
hasta el componente `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) Intentando mostrarresources
en la pantalla al encadenarlo para ver qué hay realmente dentro de este objeto:
render() {
console.log('resources', this.props);
return (
<div>
This is showcase {JSON.stringify(this.props.resources)}
</div>
);
}
Mira esto en la pantalla:This is showcase {}
. El componente no parece volver a renderizarse.
Aquí está la captura de pantalla de la consola que muestra que los accesorios de la aplicación se han actualizado con los valores delnext state
. Aún así, eso no causó que el componente se volviera a representar:
ACTUALIZADO DE NUEVO: Y mi javascript-fu también era pobre. No me di cuenta del todo al regresarObject.assign (state, {resources: action.payload.data });
De hecho, estaba mutando el estado, y que una simple inversión de argumentos me permitiría lograr lo que pretendía. Gracias aesta discusión en SO para la iluminación.