2015-11-13 4 views
5

Sono ancora molto un reagente/ridondante noob. In una pagina ho un sacco di input di testo. Dopo un po 'ho iniziato a notare il mio file azione ha funzioni che fanno la stessa cosa, ma per i diversi ingressi:Condivisione di azioni e riduttori con reazione/ridondanza

export function setInputName(string) { 
    return { 
    type: 'SET_CURRENT_NAME', 
    payload: {value: string} 
    }; 
} 
export function setInputCity(string) { 
    return { 
    type: 'SET_CURRENT_CITY', 
    payload: {value: string} 
    }; 
} 

Con la mia riduttore di ricerca come:

export function currentName(state='', {type, payload}) { 
    switch (type) { 
    case 'SET_CURRENT_NAME': 
    return payload.value; 
    default: 
    return state; 
    } 
} 
export function currentCity(state='', {type, payload}) { 
    switch (type) { 
    case 'SET_CURRENT_CITY': 
    return payload.value; 
    default: 
    return state; 
    } 
} 

E la mia componente avuto questi ingressi multipli:

import {Component, PropTypes} from 'react'; 
import {connect} from 'react-redux'; 
import {bindActionCreators} from 'redux'; 
import {setInputName, setInputCity} from 'actions/form'; 

export default class Form extends Component { 
    static propTypes = { 
    setInputName: PropTypes.func.isRequired, 
    setInputCity: PropTypes.func.isRequired, 
    currentName: PropTypes.string.isRequired, 
    currentCity: PropTypes.string.isRequired 
    } 
    render() { 
    let {setInputName, setInputCity, currentName, currentCity} = this.props; 
    return (
     <div> 
     <input 
      type="text" 
      placeholder="Name" 
      onChange={(e) => setInputName(e.currentTarget.value)} 
      value={currentName} 
     /> 
     <input 
      type="text" 
      placeholder="City" 
      onChange={(e) => setInputCity(e.currentTarget.value)} 
      value={currentCity} 
     /> 
     </div> 
    ); 
    } 
} 

function select(state) { 
    return { 
    currentName: state.form.currentName, 
    currentCity: state.form.currentCity 
    }; 
} 

function actions(dispatch) { 
    return bindActionCreators({ 
    setInputName: setInputName, 
    setInputCity: setInputCity, 
    }, dispatch); 
} 

export default connect(select, actions)(Form); 

Quale non è molto ASCIUTTO e ho subito pensato che stavo facendo qualcosa di sbagliato. C'è un buon modo per avere un'azione comune setInputValue per tutti gli input di testo su ogni pagina e componente della mia app? Vorrei anche usare un riduttore comune per ogni input. Grazie.

UPDATE

Ecco il mio esempio lordo che funziona, ma mi sento come questo è ancora un po 'contorta e ci deve essere un modo migliore. Fondamentalmente nel riduttore controllo per vedere se quell'input è stato usato e aggiunto allo stato ancora. Se no, lo aggiungo. Se è così, aggiorno solo il suo valore. EDIT Ho mentito che questo in realtà non funziona.

// actions 
export function updateTextInput(name, value) { 
    return { 
    type: 'SET_CURRENT_TEXT_INPUT', 
    payload: {name: name, value: value} 
    }; 
} 

// reducer 
export function currentTextInput(state=[], {type, payload}) { 
    switch (type) { 
    case 'SET_CURRENT_TEXT_INPUT': 
    let newState = state; 
    let curInput = state.findIndex(function(elem) { 
     return elem.name === payload.name; 
    }); 
    if (curInput === -1) { 
     newState.push({name: payload.name, value: payload.value}); 
    } else { 
     newState[curInput].value = payload.value; 
    } 
    return newState; 
    default: 
    return state; 
    } 
} 

// component 
... 
render() { 
    let {updateTextInput, currentTextInput} = this.props; 
    return (
    <div> 
     <input 
     type="text" 
     placeholder="Name" 
     onChange={(e) => updateTextInput('name', e.currentTarget.value)} 
     value={currentTextInput.name} 
     /> 
     <input 
     type="text" 
     placeholder="City" 
     onChange={(e) => updateTextInput('city', e.currentTarget.value)} 
     value={currentTextInput.city} 
     /> 
    </div> 
); 
} 
... 

risposta

5

Ok ecco un esempio come richiesto. Diciamo che ho un modello di dati utente, sembra qualcosa di simile:

{ 
    id: 1, 
    name: 'Some Name', 
    email: '[email protected]', 
    age: 21 
} 

Essi modo attualmente averla messa a punto che ci si deve avere SET_USER_NAME, SET_USER_EMAIL, azioni SET_USER_AGE. Non c'è bisogno di tutti quelli quando è possibile avere un'azione UPDATE_USER che riceve un argomento come oggetto dei valori aggiornati.

Per impostare un nuovo nome per il primo utente che si chiamerà l'azione UPDATE_USER come

updateUser(1, { name: 'New Name' })

In Utenti riduttore che ci si aggiorna lo stato di utenti (una serie di utenti) come

function updateUser(usersState, id, attrs) { 
    return usersState.map(user => 
    user.id === id ? 
    Object.assign({}, user, attrs) : 
    user 
); 
} 
+0

Questo chiarisce un po 'le cose. Cosa accadrebbe se avessi più moduli non correlati su una pagina e non avessi un modello specifico da cui partire? Potrei comunque utilizzare un comune azione/riduttore in qualche modo? Senza avere un modello gianormous per l'intera pagina. –

+3

Ogni modello dovrebbe avere il proprio riduttore che riceve la propria porzione di stato. Potresti comunque avere una singola funzione di aggiornamento che può essere importata e utilizzata nei vari riduttori – slightlytyler

6

Un piccolo primo passo per semplificare il codice consiste nell'utilizzare riduttori/funzioni di ordine superiore.

function makePropReducer(actionType, prop) { 
    return (state = '', {type, payload}) => { 
    if (type === actionType) { 
     return payload[prop]; 
    } 
    return state; 
    }; 
} 

export const currentName = makePropReducer('SET_CURRENT_NAME', 'value'); 
export const currentCity = makePropReducer('SET_CURRENT_CITY', 'value'); 
+0

Questo è fantastico. Sicuramente usando questo. Grazie –

1

di recente mi si avvicinò con tale azione,

export function controlStateChange(controlName, propertyName, value) { 
    return { type: 'CONTROL_STATE_CHANGE', payload: { controlName, propertyName, value } }; 
} 

e riduttore correspoding,

const actionHandlers = { 
    'CONTROL_STATE_CHANGE': (state, payload) => { 
    const controls = { 
     [payload.controlName]: { 
     [payload.propertyName]: payload.value 
     } 
    }; 

    return {...state, controls }; 
    } 
}; 

E 'del tutto generale, e spero che potrebbe servire bisogno di te troppo.

0

Per salvare ingresso a stato, è possibile utilizzare redux-form

Sembra la tua domanda non è in realtà legato alla condivisione di riduttori/azioni. Per condividere azioni/riduzioni, ne ho scritto uno e puoi dare un'occhiata redux-conditional