javascript - funciona - redux react native español
RactiveJS+Redux despacha acciones e hidrata (1)
Estoy intentando configurar RactiveJS con Redux para una pequeña aplicación de ejemplo: inicializar el panel (desde AJAX), agregar / eliminar elementos (widgets) desde el panel (y guardar datos serializados en el servidor). Como hay tutoriales casi exclusivamente para React, entonces necesito consejos. Seguí algunos y obtuve una estructura de directorios como:
views
app.html
dashboard.html
widget.html
js
actions
DashboardActions.js
components
Dashboard.js
Widget.js
constants
ActionTypes.js
reducers
dashboard.js
index.js
app.js
index.html
Este ejemplo funciona, pero hay varios problemas y me gustaría descubrir cómo hacerlo mejor. Por ejemplo:
1) ¿Cómo pasar (y debo pasar?) Almacenar y acciones hasta el árbol de componentes de Ractive? En este momento usa bindActionCreators
en cada componente y creo que esta no es una buena solución.
2) ¿Dónde poner la hidratación inicial del estado del servidor? En este momento, está codificado en reducers/dashboard.js
, pero me gustaría utilizar el backend como fuente de datos y punto final para guardar datos. Hay un enfoque de middleware, pero si esto es una buena práctica, ¿cómo aplicar eso con RactiveJs?
3) ¿Debo usar un reducer
grande o por cada componente un reducer
?
4) ¿Tal vez el concepto central es incorrecto y debe ser refactorizado?
views / app.html
<Dashboard dashboard={{store.getState()}} store="{{store}}"></Dashboard>
views / dashboard.html
{{#with dashboard}}
<pre>
====
<a on-click="@this.addWidget(''Added by click'')" href="#">Add New</a>
{{#dashboard}}
{{#each widgets}}
<Widget id="{{this.id}}" name="{{this.name}}" size="{{this.size}}" actions="{{actions}}" store="{{store}}"></Widget>
{{/each}}
{{/dashboard}}
====
</pre>
{{/with}}
views / widget.html
<div>{{id}}-{{name}} (Size: {{size}})<a href="#" on-click="@this.deleteWidget(id)">X</a></div>
acciones / DashboardActions.js
import * as types from ''../constants/ActionTypes'';
// Add widget to dashboard
export function addWidget(name) {
return {
type: types.ADD_WIDGET,
name
};
}
// Delete widget from dashboard
export function deleteWidget(id) {
return {
type: types.DELETE_WIDGET,
id
};
}
componentes / Dashboard.js
import Ractive from ''ractive''
import * as DashboardActions from ''../actions/DashboardActions'';
import { dispatch, bindActionCreators } from ''redux''
import Widget from ''./Widget''
import template from ''../../views/dashboard.html'';
export default Ractive.extend({
isolated: true,
components: {
Widget
},
oninit() {
const store = this.get("store");
const actions = bindActionCreators(DashboardActions, store.dispatch);
this.set("actions", actions);
},
addWidget(name) {
this.get("actions").addWidget(name);
},
template: template
});
componentes / Widget.js
import Ractive from ''ractive''
import * as DashboardActions from ''../actions/DashboardActions'';
import { dispatch, bindActionCreators } from ''redux''
import template from ''../../views/widget.html'';
export default Ractive.extend({
isolated: true,
template: template,
oninit() {
console.log(this.get("actions"));
const store = this.get("store");
const actions = bindActionCreators(DashboardActions, store.dispatch);
this.set("actions", actions);
},
deleteWidget(id) {
this.get("actions").deleteWidget(id);
},
})
constantes / ActionTypes.js
// Add widget to dashboard
export const ADD_WIDGET = ''ADD_WIDGET'';
// Delete widget from dashboard
export const DELETE_WIDGET = ''DELETE_WIDGET'';
reducrs / dashboard.js
import * as types from ''../constants/ActionTypes'';
const initialState = {
widgets: [
{id: 1, name: "First widget"},
{id: 2, name: "Second widget"},
{id: 3, name: "Third widget"},
],
};
export default function dashboard(state = initialState, action) {
switch (action.type) {
case types.ADD_WIDGET:
const newId = state.widgets.length + 1;
const addedWidgets = [].concat(state.widgets, {
id: newId,
name: action.name
});
return {
widgets: addedWidgets
}
case types.DELETE_WIDGET:
const newWidgets = state.widgets.filter(function(obj) {
return obj.id != action.id
});
return {
widgets: newWidgets
}
default:
return state;
}
}
reducrs / index.js
export { default as dashboard } from ''./dashboard'';
app.js
import Ractive from ''ractive'';
import template from ''../views/app.html'';
import Dashboard from ''./components/Dashboard.js''
import { createStore, combineReducers, bindActionCreators } from ''redux''
import * as reducers from ''./reducers''
const reducer = combineReducers(reducers);
const store = createStore(reducer);
let App = new Ractive({
el: ''#app'',
template: template,
components: {
Dashboard
},
data: {
store
}
});
store.subscribe(() => App.update());
export default App;
¡Gracias!
Ractive no impone ninguna convención sobre cómo se hace esto. Sin embargo, Ractive está diseñado de forma similar a otros marcos (anzuelos, métodos, etc.) del ciclo de vida. Entonces, lo que funciona para ti en otros frameworks también debería funcionar en Ractive.
¿Cómo pasar (y debería pasar?) Almacenar y acciones hasta el árbol de componentes de Ractive? En este momento usa bindActionCreators en cada componente y creo que esta no es una buena solución.
Tal vez el concepto básico es incorrecto y debe ser refactorizado?
Estoy bastante seguro de que no sabe si asignar tiendas y acciones directamente a los componentes o pasarlos a través de antepasados. La respuesta es ... ambas. El autor de Redux realmente divide los componentes en 2 tipos : presentacionales y contenedores.
En esencia, los componentes del contenedor mantienen las acciones de estado y llamada. Los componentes de presentación son sin estado y reciben material de los componentes ancestrales.
Supongamos que tiene un widget meteorológico que muestra la temperatura y las condiciones. Tendría 3 componentes, el componente del widget en sí, la temperatura y las condiciones. Ambos componentes de temperatura y condiciones son de presentación. El componente meteorológico será el contenedor que capture los datos, los entregue a ambos componentes y también transforme la interacción de la interfaz de usuario en acciones.
Weather.js
// Assume the store is in store.js with actions already registered
import store from ''./path/to/store'';
import Temperature from ''./path/to/Temperature'';
import Conditions from ''./path/to/Conditions'';
export default Ractive.extend({
components: { Temperature, Conditions },
template: `
<div class="weather">
<!-- pass in state data to presentational components -->
<!-- call methods when events happen from components -->
<Temperature value="{{ temperature }}" on-refresh="refreshTemp()" />
<Conditions value="{{ conditions }}" on-refresh="refreshCond()" />
</div>
`,
data: {
temperature: null,
conditions: null
},
oninit(){
store.subscribe(() => {
// Grab state and set it to component''s local state
// Assume the state is an object with temperature and
// conditions properties.
const { temperature, conditions } = store.getState();
this.set({ temperature, conditions });
});
},
// Call actions
refreshTemp(){
store.dispatch({ type: ''TEMPERATURE_REFRESH'' });
},
refreshCond(){
store.dispatch({ type: ''CONDITIONS_REFRESH'' });
}
});
Temperature.js
// This component is presentational. It is not aware of Redux
// constructs at all. It only knows that it accepts a value and
// should fire refresh.
export default Ractive.extend({
template:`
<div class="temperature">
<span>The temperature is {{ value }}</span>
<button type="button" on-click="refresh">Refresh</button>
</div>
`
});
Condicioness.js
// This component is presentational. It is not aware of Redux
// constructs at all. It only knows that it accepts a value and
// should fire refresh.
export default Ractive.extend({
template:`
<div class="conditions">
<img src="http://localhost/condition-images/{{ value }}.jpg">
<button type="button" on-click="refresh">Refresh</button>
</div>
`
});
¿Dónde colocar la hidratación inicial del estado del servidor?
Si mal no recuerdo, un flujo de trabajo isomorfo que vi involucró poner el estado provisto por el servidor en una variable global cuidadosamente nombrada. Al inicio de la aplicación, la aplicación recoge los datos en ese global y los introduce en la tienda. Ractive no está involucrado en este proceso.
Esto será impreso por su servidor en la página:
<script>
window.__APP_INITIAL_STATE__ = {...};
</script>
Luego, cuando inicias la aplicación, creas una tienda usando ese estado inicial:
import { createStore } from ''redux''
import reducers from ''./reducers''
let store = createStore(reducers, window.__APP_INITIAL_STATE__);
¿Debo usar un reductor grande o por cada componente un reductor?
Redux tiene una buena guía sobre cómo dividir reductores y cómo normalizar la forma del estado . En general, la forma del estado no está definida por componente sino más por funcionalidad.