2016-04-21 5 views
7

Attualmente sto cercando di imparare Redux e penso di doverlo implementare in modo errato in quanto solo l'ultimo componente da rendere sulla pagina funziona effettivamente. Sospetto che lo stato venga sovrascritto ogni volta.Lo stato sembra essere sovrascritto invece di unire

Ho seguito il tutorial this che utilizza solo un esempio di recupero dei dati, quindi probabilmente ho appena frainteso come scrivere nello stato.

Ecco il mio riduttore. Qualche idea in cui ho sbagliato? Anche questo sembra molto prolisso, c'è un modo DRYer di scrivere questo?

import Immutable from 'seamless-immutable'; 
import { combineReducers } from 'redux'; 
import { routerReducer } from 'react-router-redux'; 

// Import actions 
import { 
    REQUEST_CONFIG, 
    GET_CONFIG, 
    REQUEST_PAGES, 
    GET_PAGES, 
    REQUEST_SOCIAL_LINKS, 
    GET_SOCIAL_LINKS, 
    REQUEST_NAV, 
    GET_NAV 
} from './actions'; 

const configInitialState = Immutable({ 
    items: [], 
    isFetching: false 
}) 

const pagesInitialState = Immutable({ 
    items: [], 
    isFetching: false 
}) 

const socialLinksInitialState = Immutable({ 
    items: [], 
    isFetching: false 
}) 

const navInitialState = Immutable({ 
    items: [], 
    isFetching: false 
}) 

export function config(state = configInitialState, action) { 
    switch (action.type) { 
    case GET_CONFIG : 
     return Immutable(state).merge({ 
     items: action.payload.config[0], 
     isFetching: false 
     }) 
    case REQUEST_CONFIG : 
     return Immutable(state).merge({ 
     isFetching: true 
     }) 
    default: 
     return state; 
    } 
} 

export function pages(state = pagesInitialState, action) { 
    switch (action.type) { 
    case GET_PAGES : 
     return Immutable(state).merge({ 
     items: action.payload.pages, 
     isFetching: false 
     }) 
    case REQUEST_PAGES : 
     return Immutable(state).merge({ 
     isFetching: true 
     }) 
    default: 
     return state; 
    } 
} 

export function socialLinks(state = socialLinksInitialState, action)  { 
    switch (action.type) { 
    case GET_SOCIAL_LINKS : 
     return Immutable(state).merge({ 
     items: action.payload.socialLinks, 
     isFetching: false 
     }) 
    case REQUEST_SOCIAL_LINKS : 
     return Immutable(state).merge({ 
     isFetching: true 
     }) 
    default: 
     return state; 
    } 
} 

export function nav(state = navInitialState, action) { 
    switch (action.type) { 
    case GET_NAV : 
     return Immutable(state).merge({ 
     items: action.payload.nav, 
     isFetching: false 
     }) 
    case REQUEST_NAV : 
     return Immutable(state).merge({ 
     isFetching: true 
     }) 
    default: 
     return state; 
    } 
} 

const rootReducer = combineReducers({ 
    config, 
    pages, 
    socialLinks, 
    nav, 
    routing: routerReducer 
}); 

export default rootReducer; 

Solo nel caso è richiesto, ecco un esempio di uno dei miei componenti così:

import React from 'react'; 
import { Link } from 'react-router'; 
import { connect } from 'react-redux'; 
import { navLoad } from '../../../scripts/actions'; 

export default class HeaderNav extends React.Component { 
    componentWillMount() { 
    const { dispatch } = this.props; 
    dispatch(navLoad()); 
    } 

    render() { 
    const { nav } = this.props; 
    const navitems = nav && nav.items ? nav.items.asMutable().map((item) => { 
     if(item.inNav === 'header' || item.inNav === 'both') { 
     return <li key={item._id}><Link to={item.slug}>{item.name}</Link></li> 
     } 
    }) : null; 
    if(nav.isFetching) { 
     return(
     <section class="loader"> 
      <span>Content is loading...</span> 
     </section> 
    ) 
    } else { 
     return(
     <nav class="c-primary-nav"> 
      <span class="c-primary-nav_toggle">Menu</span> 
      <ul> 
      { navitems } 
      </ul> 
     </nav> 
    ) 
    } 
    } 
} 

function select(state) { 
    const { nav } = state; 
    return { 
    nav 
    }; 
} 

export default connect(select)(HeaderNav); 

e qui è dove i componenti si chiamano:

import React from 'react'; 
import HeaderNav from './Header/nav.jsx'; 
import SocialLinks from './Header/socialLinks.jsx'; 

export default class Header extends React.Component { 
    render() { 
    return(
     <header class="c-global-header" role="banner"> 
     <HeaderNav /> 
     <SocialLinks /> 
     </header> 
    ) 
    } 
} 

Aggiornamento: Sto ancora sperando in una risposta a questa domanda perché mi sta ancora bloccando, ho modificato il codice un po 'ora ma senza successo. Puoi vedere il mio full codebase qui: https://github.com/alexward1981/portfolio-react (Tutte le cose rilevanti sono nella cartella 'src')

+0

Puoi spiegare il problema in modo più dettagliato? Cosa intendi quando dici "solo l'ultimo componente da renderizzare funziona"? Cosa sta succedendo/non funziona in modo specifico? – azium

+0

Quindi la mia pagina esegue il rendering di due componenti,

+0

@AlexWard un problema che vedo è che stai esportando due membri nel tuo file come esportazioni predefinite: 'Esporta la classe predefinita HeaderNav estende React.Component' e' esportazione predefinita connect (seleziona) (HeaderNav); '. Solo uno dovrebbe essere esportato come predefinito (nel tuo caso, di sicuro è il secondo). In TypeScript, si otterrebbe un errore, ma probabilmente usando Babel, esso semplicemente esporterà come predefinito le prime esportazioni predefinite trovate nel file, e probabilmente questo è il motivo per cui le cose non funzionano per te. – Buzinas

risposta

0

Se il tuo problema è che vuoi ottenere nuovi dati per un componente quando la pagina si aggiorna, allora il recupero dei dati in componentWillMount() non è sufficiente (perché è chiamato solo al 1 ° rendering del componente).

È inoltre necessario aggiungere dati-fetching nel metodo del ciclo di vita di un altro componente, componentWillReceiveProps():

componentWillReceiveProps(nextProps) { 
    const { dispatch } = this.props; 
    dispatch(navLoad()); 
} 

Si potrebbe aggiungere una condizione adeguata (a confronto con nextProps this.props) per verificare se davvero bisogno di recuperare nuovi dati su ogni aggiornamento.

+0

Non c'è un aggiornamento di pagina secondario, tutti i componenti sono richiamati sul caricamento della prima pagina, tuttavia solo l'ultimo componente della pagina è quello che viene effettivamente montato. –

0

Non ho dimestichezza con immutabile senza interruzioni, ma il problema potrebbe essere correlato al suo utilizzo nei riduttori. Sei sicuro che questa libreria produca realmente nuovi riferimenti a oggetti quando lo stato cambia, come richiesto da redux?

In ogni caso, potrebbe essere necessario utilizzare più codice per combinare correttamente il redux e l'immutabile senza interruzioni, come fa this library.

Per semplificare le cose, penso che dovresti provare ad usare semplici modelli di immutabilità di JS (con Object.assign(), o la sintassi di diffusione dell'oggetto per esempio) invece di immutabile senza interruzioni. Ad esempio, è possibile scrivere i riduttori di simile a questo:

const navInitialState = { 
    items: [], 
    isFetching: false 
} 

function nav(state = navInitialState, action) { 
    switch (action.type) { 
    case GET_NAV : 
     return { 
     ...state, 
     items: action.payload.nav, 
     isFetching: false 
     } 
    case REQUEST_NAV : 
     return { 
     ...state, 
     isFetching: true 
     } 
    default: 
     return state 
    } 
} 
0

E 'stato un errore di battitura sanguinante dopo tutto quello che ho appena mai notato. Grazie per l'aiuto però.

+0

Prego! Sono contento che alla fine hai risolto il tuo problema. Ciao ! –

+0

Qual è stato l'errore? Sto affrontando lo stesso numero –

+0

In un'altra pagina in cui importa le azioni, ho dimenticato per sbaglio di rinominare alcune di esse in modo che sovrascrivessero quelle precedenti con dati errati (ad esempio dati vuoti) –

4

Ho appena affrontato lo stesso problema, il problema era nel riduttore, invece dello stato di ritorno nella sezione caso interruttore di default, sono stato restituito initialState.

export const getAppShopReducer = (state = initialState, action) => { 
    switch (action.type) { 
    case types.FETCH_APP_SHOP_SUCCESS: 

     return state 
     .set('loading', false) 
     .set('user', action.payload.user) 
     .set('appShop', action.payload.appShop) 
     .set('shop', action.payload.shop) 
     ; 

    case types.FETCH_APP_SHOP_REQUEST: 

     return initialState.set('loading', true); 

    default: 
     return state; // Here's the problem, it was `initialState`; 
    } 
}; 
0

sono stato in grado di lavorare con il mio problema con la lettura attraverso questa lista nelle note sulla combineReducers

http://redux.js.org/docs/api/combineReducers.html#notes

Ci sono alcuni casi in cui non riesce automaticamente se undefined stato isnt gestito correttamente.

Problemi correlati