Estoy tratando de separar un componente de presentación de un componente de contenedor. Tengo un SitesTable
y un SitesTableContainer
. El contenedor es responsable de desencadenar acciones redux para buscar los sitios apropiados según el usuario actual.
El problema es que el usuario actual se recupera de forma asincrónica, después de que el componente contenedor se procesa inicialmente. Esto significa que el componente contenedor no sabe que necesita volver a ejecutar el código en su componentDidMount
función, lo que actualizaría los datos para enviarlos a SitesTable
. Creo que necesito volver a renderizar el componente contenedor cuando cambia uno de sus accesorios (usuario). ¿Cómo hago esto correctamente?
class SitesTableContainer extends React.Component {
static get propTypes() {
return {
sites: React.PropTypes.object,
user: React.PropTypes.object,
isManager: React.PropTypes.boolean
}
}
componentDidMount() {
if (this.props.isManager) {
this.props.dispatch(actions.fetchAllSites())
} else {
const currentUserId = this.props.user.get('id')
this.props.dispatch(actions.fetchUsersSites(currentUserId))
}
}
render() {
return <SitesTable sites={this.props.sites}/>
}
}
function mapStateToProps(state) {
const user = userUtils.getCurrentUser(state)
return {
sites: state.get('sites'),
user,
isManager: userUtils.isManager(user)
}
}
export default connect(mapStateToProps)(SitesTableContainer);
reactjs
redux
react-redux
David
fuente
fuente
componentDidMount
cambiarán elsites
nodo en el estado de la aplicación, que se pasa alSitesTable
. Elsites
nodo de SitesStable cambiará.Respuestas:
Tienes que agregar una condición en tu
componentDidUpdate
método.El ejemplo está usando
fast-deep-equal
para comparar los objetos.import equal from 'fast-deep-equal' ... constructor(){ this.updateUser = this.updateUser.bind(this); } componentDidMount() { this.updateUser(); } componentDidUpdate(prevProps) { if(!equal(this.props.user, prevProps.user)) // Check if it's a new user, you can also use some unique property, like the ID (this.props.user.id !== prevProps.user.id) { this.updateUser(); } } updateUser() { if (this.props.isManager) { this.props.dispatch(actions.fetchAllSites()) } else { const currentUserId = this.props.user.get('id') this.props.dispatch(actions.fetchUsersSites(currentUserId)) } }
Usando Hooks (React 16.8.0+)
import React, { useEffect } from 'react'; const SitesTableContainer = ({ user, isManager, dispatch, sites, }) => { useEffect(() => { if(isManager) { dispatch(actions.fetchAllSites()) } else { const currentUserId = user.get('id') dispatch(actions.fetchUsersSites(currentUserId)) } }, [user]); return ( return <SitesTable sites={sites}/> ) }
Si el accesorio que está comparando es un objeto o una matriz, debe usar en
useDeepCompareEffect
lugar deuseEffect
.fuente
user
cambie? ¿Qué tan caro es esto?ComponentWillReceiveProps()
va a quedar obsoleto en el futuro debido a errores e inconsistencias. Una solución alternativa para volver a renderizar un componente en el cambio de accesorios es usarComponentDidUpdate()
yShouldComponentUpdate()
.ComponentDidUpdate()
se llama siempre que el componente se actualiza Y siShouldComponentUpdate()
devuelve verdadero (siShouldComponentUpdate()
no está definido, vuelvetrue
de forma predeterminada).shouldComponentUpdate(nextProps){ return nextProps.changedProp !== this.state.changedProp; } componentDidUpdate(props){ // Desired operations: ex setting state }
Este mismo comportamiento se puede lograr usando solo el
ComponentDidUpdate()
método incluyendo la declaración condicional dentro de él.componentDidUpdate(prevProps){ if(prevProps.changedProp !== this.props.changedProp){ this.setState({ changedProp: this.props.changedProp }); } }
Si uno intenta establecer el estado sin un condicional o sin definir,
ShouldComponentUpdate()
el componente se volverá a renderizar infinitamentefuente
componentWillReceiveProps
está a punto de quedar obsoleta y se sugiere que no se use.Puede usar
KEY
una clave única (combinación de los datos) que cambia con los accesorios, y ese componente se volverá a representar con los accesorios actualizados.fuente
componentWillReceiveProps(nextProps) { // your code here}
Creo que ese es el evento que necesitas.
componentWillReceiveProps
se activa cada vez que su componente recibe algo a través de accesorios. A partir de ahí, puede tener su control y luego hacer lo que quiera hacer.fuente
componentWillReceiveProps
obsoleto *Recomendaría echar un vistazo a esta respuesta mía y ver si es relevante para lo que está haciendo. Si entiendo tu problema real, es que simplemente no estás usando tu acción asincrónica correctamente y actualizando la "tienda" de redux, que actualizará automáticamente tu componente con sus nuevos accesorios.
Esta sección de su código:
componentDidMount() { if (this.props.isManager) { this.props.dispatch(actions.fetchAllSites()) } else { const currentUserId = this.props.user.get('id') this.props.dispatch(actions.fetchUsersSites(currentUserId)) } }
No debe activarse en un componente, debe manejarse después de ejecutar su primera solicitud.
Eche un vistazo a este ejemplo de redux-thunk :
function makeASandwichWithSecretSauce(forPerson) { // Invert control! // Return a function that accepts `dispatch` so we can dispatch later. // Thunk middleware knows how to turn thunk async actions into actions. return function (dispatch) { return fetchSecretSauce().then( sauce => dispatch(makeASandwich(forPerson, sauce)), error => dispatch(apologize('The Sandwich Shop', forPerson, error)) ); }; }
No necesariamente tiene que usar redux-thunk, pero lo ayudará a razonar sobre escenarios como este y escribir código para que coincida.
fuente
makeASandwichWithSecretSauce
en su componente?Un método amigable para usar es el siguiente, una vez que se actualice la propiedad, automáticamente volverá a renderizar el componente:
render { let textWhenComponentUpdate = this.props.text return ( <View> <Text>{textWhenComponentUpdate}</Text> </View> ) }
fuente