contexto
Estoy representando un formulario con un conjunto dinámico de elementos de texto. He normalizado mi estado utilizando los principios de normalizr, por lo que hay una matriz de elementIds y un objeto que contiene las propiedades del elemento a las que hacen referencia los elementIds (consulte el estado inicial en el ejemplo de código a continuación).
apuntar
Mi objetivo es simplemente que los dos elementos renderizados sean editables. Estoy enviando con éxito una acción CHANGE_ELEMENT_VALUE a mi tienda usando una devolución de llamada onChange, y action.id (que hace referencia a la identificación del elemento modificado) y action.value (el nuevo valor) están disponibles en el reductor (vea el código a continuación).
problema
Mi problema es que los campos de texto no cambian cuando escribo, aunque puedo ver cambios de estado usando la extensión devtools redux. Tengo entendido que reaccionar no reconoce un cambio de estado porque el cambio es profundo en el estado, y no estoy creando con éxito un nuevo objeto de estado, probablemente estoy haciendo referencia a instancias antiguas de alguna manera.
código reductor
A continuación se muestra mi fallido intento de forzar un nuevo objeto de estado. Supongo que no funciona porque mis componentes no se vuelven a renderizar. También parece muy poco elegante.
let initialState = { isLoading: false, data: { elementIds: ['name', 'email'], elements: { 'name': {'id': 'name', 'value':'ben'}, 'email': {'id':'email', 'value':'ben@test.com'}, }, }, error: false } function formReducer(state = initialState, action = null) { switch(action.type) { case types.CHANGE_ELEMENT_VALUE: let newData = Object.assign(state.data) newData.elements[action.id].value = action.value return {...state, data: newData} default: return state; } } export default formReducer;
puede utilizar immutability-helper npm package
y actualizar sus valores en su reductor
import update from 'immutability-helper'; let initialState = { isLoading: false, data: { elementIds: ['name', 'email'], elements: { 'name': {'id': 'name', 'value':'ben'}, 'email': {'id':'email', 'value':'ben@test.com'}, }, }, error: false } function formReducer(state = initialState, action = null) { switch(action.type) { case types.CHANGE_ELEMENT_VALUE: return update(state, { data : { elements: { [action.id]: { value: { $set: 'new value' } } } } }) default: return state; } } export default formReducer;
update()
proporciona azúcar sintáctico simple alrededor de este patrón para facilitar la escritura de este código. Si bien se necesita un poco de tiempo para acostumbrarse a la sintaxis (aunque está inspirada en el lenguaje de consulta de MongoDB), no hay redundancia, es analizable estáticamente y no requiere mucho más tipeo que la versión mutativa.
Object.assign
solo opera un nivel de profundidad; es decir, no clona recursivamente todo el árbol de objetos. Por lo tanto, su objeto de nivel superior está clonado pero no activa una nueva representación ya que su reductor muta un valor en lo profundo del objeto clonado.
Recomendaría buscar en el paquete deep-extend
y actualizar su estado de la siguiente manera:
import extend from 'deep-extend'; ... return extend(state, { elements: { [key]: value } });