2016-04-14 10 views
10

Sto lavorando all'applicazione utilizzando react & redux e ho bisogno di setProps ma è deprecato.Come evitare di usare setProps in React?

Date un'occhiata l'errore di seguito:

Attenzione: SetProps (...) e (...) replaceProps sono deprecati. Invece, chiama nuovamente il rendering al livello superiore.

Violazione invariante non rilevata: setProps (...): Hai chiamato setProps su un componente con un genitore. Si tratta di un anti-pattern dal momento che gli oggetti di scena saranno aggiornati durante il rendering. Invece, cambiare il metodo render del proprietario per passare il valore corretto come oggetti di scena al componente dove è stato creato.

Quindi, come posso impostare gli oggetti di scena? in fondo è quello di gestire alcune schede, date un'occhiata qui sotto il mio codice:

export default React.createClass({ 
    render: function() { 
    return (
     <div className="demo-tabs"> 
      <Tabs activeTab={this.props.activeTab} 
        onChange={(tabId) => this.setProps({ activeTab: tabId })} ripple> 
       <Tab>Stack</Tab> 
       <Tab>GitHub</Tab> 
       <Tab>Twitter</Tab> 
      </Tabs> 
      <section> 
       <div className="content">Content for the tab: {this.props.activeTab}</div> 
      </section> 
     </div> 
    ); 
    } 
}); 

Container

import React from 'react'; 
import TimeLine from './timeline'; 
import { connect } from 'react-redux'; 
import { getStackoverflow } from 'api/timeline'; 

const TimeLineContainer = React.createClass({ 

    componentWillMount: function() { 
     getStackoverflow(); 
    }, 

    render: function() { 
     return (
      <TimeLine {...this.props} /> 
     ) 
    } 
}); 

const stateToProps = function(state) { 
    return { 
     timeline: state.timelineReducer.timeline 
    } 
} 

const dispatchToProps = function(dispatch) { 
    return { 
     onClick:() => {console.log('timeline was clicked')} 
    } 
} 

export default connect(stateToProps, dispatchToProps)(TimeLineContainer) 

Reducer

var timelineInitialState = { 
    github:   [], 
    gist:   [], 
    stackoverflow: [], 
    twitter:  [], 
    activeTab:  2 
} 

export default function(state = timelineInitialState, action) { 
    switch (action.type) { 
    case 'GET_GITHUB': 
     //TODO: implement. 
     break; 
    case 'GET_GIST': 
     //TODO: implement. 
     break; 
    case 'GET_STACKOVERFLOW': 
     var stackoverflowState = Object.assign({}, state) 
     stackoverflowState.stackoverflow = action.stackoverflow; 
     return stackoverflowState; 
     break; 
    case 'GET_TWITTER': 
     //TODO: implement. 
     break; 
    default: 
     return state; 
    } 
} 
+2

Sembra che gli oggetti di scena che è necessario impostare debbano essere nello stato interno e si dovrebbe invece chiamare setState. O tieni il puntello ma fallo impostare lo stato quando cambia (sync prop e state) –

+0

Come ha detto @JuanMendes, vorrai usare lo stato. Quello che puoi fare è impostare il tuo stato iniziale usando i tuoi oggetti di scena e andando avanti imposta il valore aggiornato per dichiarare –

+0

@realseanp devi assicurarti di implementare 'componentWillReceiveProps' in modo che aggiorni lo stato quando il componente genitore imposta un puntello dopo l'iniziale rendere –

risposta

44

Sembra che si tuffò usando Redux senza prima ottenere una presa sicura di reagire. Non consiglierei di farlo perché ora sembra un po 'confuso.

Thinking in React è un'ottima guida e ti consiglio di analizzarlo prima e di familiarizzare con l'idea di proprietà statale in React, prima di utilizzare Redux.

In React, le cose che cambiano nel tempo ("stato") sono sempre "possedute" da qualche componente. A volte è lo stesso componente che usa questo stato per il rendering. A volte molti componenti devono essere sincronizzati in modo che lo stato venga "issato" su un componente principale che possa gestirli tutti e passare tali informazioni tramite oggetti di scena. Ogni volta che lo stato cambia, vengono tutti aggiornati.

La parte importante qui è che props devono essere ricevuti dal genitore.Se un componente vuole cambiare i propri oggetti di scena, si tratta di un sintomo di un problema:

  1. O si dovrebbe avere utilizzato state per questo componente, in modo da poter chiamare setState
  2. O il componente deve accedere a un callback prop come onChange in modo che possa “chiedere” il valore da modificare

Nel primo caso, il codice sarebbe simile a questa, e sarebbe valida Reagire:

export default React.createClass({ 
    getInitialState: function() { 
    return { activeTab: 0 } 
    }, 

    render: function() { 
    return (
     <div className="demo-tabs"> 
      <Tabs activeTab={this.state.activeTab} 
        onChange={(tabId) => this.setState({ activeTab: tabId })} ripple> 
       <Tab>Stack</Tab> 
       <Tab>GitHub</Tab> 
       <Tab>Twitter</Tab> 
      </Tabs> 
      <section> 
       <div className="content">Content for the tab: {this.state.activeTab}</div> 
      </section> 
     </div> 
    ); 
    } 
}); 

Tuttavia, è possibile continuare lo schema che si sta già utilizzando con il componente <Tabs> all'interno e sollevare lo stato ancora più in alto. Quindi il tuo componente dovrebbe accettare un puntello onChange che passerebbe a <Tabs>. Ora non sa come lo stato viene aggiornato, e non regge lo Stato:

export default React.createClass({ 
    render: function() { 
    return (
     <div className="demo-tabs"> 
      <Tabs activeTab={this.props.activeTab} 
        onChange={this.props.onChange} ripple> 
       <Tab>Stack</Tab> 
       <Tab>GitHub</Tab> 
       <Tab>Twitter</Tab> 
      </Tabs> 
      <section> 
       <div className="content">Content for the tab: {this.props.activeTab}</div> 
      </section> 
     </div> 
    ); 
    } 
}); 

Nessuno dei due approcci è meglio o peggio, vengono utilizzati per scopi diversi. Il primo è più conveniente quando lo stato è irrilevante per il resto dell'app, il secondo è conveniente quando anche altri componenti distanti ne hanno bisogno. Assicurati di comprendere i compromessi di entrambi gli approcci.

Anche con il secondo approccio, qualcosa di deve contenere lo stato. Potrebbe essere un altro componente, o (ed è qui che entra in gioco Redux) potrebbe essere un'archiviazione dei dati separata come un negozio Redux. In questo caso, invece di fornire onChange da un componente genitore, si può usare connect() per associare un onChange prop inietta di dispacciamento un'azione Redux:

const mapStateToProps = (state) => { 
    return { 
    activeTabId: state.activeTabId 
    } 
} 

const mapDispatchToProps = (dispatch) => { 
    return { 
    onChange: (tabId) => dispatch({ type: 'SET_ACTIVE_TAB', tabId }) 
    } 
} 

e nella tua riduttori, è possibile gestire questa azione:

const activeTabId = (state = 0, action) => { 
    switch (action.type) { 
    case 'SET_ACTIVE_TAB': 
    return action.tabId 
    default: 
    return state 
    } 
} 

const reducer = combineReducers({ 
    activeTabId, 
    // other reducers 
}) 

const store = createStore(reducer) 

In entrambi i casi è necessario setProps. È possibile:

  • lasciare che il componente possiede lo stato e l'uso setState,
  • lasciarlo accettare le activeTabId e onChange oggetti di scena e gestirli da un componente più alto nella struttura che avrebbe utilizzato setState, o
  • puoi spostare la gestione dello stato completamente fuori dai componenti in qualcosa come Redux, ma i tuoi componenti accetterebbero ancora activeTabId e onChange come oggetti di scena.
2

Fornire onTabChange callback per componente Timeline da Container.

Contenitore: componente

const dispatchToProps = function(dispatch) { 
    return { 
     onClick:() => {console.log('timeline was clicked')}, 
     onTabChange: (tabId) => { setActiveTabAction(tabId) } 
    } 
} 
const stateToProps = function(state) { 
    return { 
     timeline: state.timelineReducer.timeline, 
     activeTab: state.timelineReducer.activeTab 
    } 
} 

Timeline:

export default React.createClass({ 
    render: function() { 
    return (
     <div className="demo-tabs"> 
      <Tabs activeTab={this.props.activeTab} 
        onChange={this.props.onTabChange} ripple> 
       <Tab>Stack</Tab> 
       <Tab>GitHub</Tab> 
       <Tab>Twitter</Tab> 
      </Tabs> 
      <section> 
       <div className="content">Content for the tab: {this.props.activeTab}</div> 
      </section> 
     </div> 
    ); 
    } 
}); 

componenti di presentazione (come componente Timeline) di solito non dovrebbe gestire lo stato dell'applicazione. Tutti i dati e le richiamate che devono ricevere dagli oggetti di scena.

vi consiglio di leggere l'articolo sui componenti di presentazione e container: https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0#.sijqpzk93

Problemi correlati