Estou tentando separar um componente de apresentação de um componente de contêiner. Eu tenho um SitesTable
e um SitesTableContainer
. O contêiner é responsável por acionar ações de redux para buscar os sites apropriados com base no usuário atual.
O problema é que o usuário atual é buscado de forma assíncrona, depois que o componente do contêiner é renderizado inicialmente. Isso significa que o componente do contêiner não sabe que precisa reexecutar o código em sua componentDidMount
função, o que atualizaria os dados a serem enviados ao SitesTable
. Acho que preciso renderizar novamente o componente do contêiner quando um de seus adereços (usuário) muda. Como faço isso corretamente?
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
fonte
fonte
componentDidMount
irão alterar osites
nó no estado do aplicativo, que é passado para oSitesTable
. Osites
nó do SitesStable será alterado.Respostas:
Você tem que adicionar uma condição em seu
componentDidUpdate
método.O exemplo está usando
fast-deep-equal
para comparar os 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 Ganchos (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}/> ) }
Se o objeto que você está comparando for um objeto ou uma matriz, você deve usar em
useDeepCompareEffect
vez deuseEffect
.fonte
user
mudar? Quão caro é isso?ComponentWillReceiveProps()
será preterido no futuro devido a bugs e inconsistências. Uma solução alternativa para renderizar novamente um componente na mudança de adereços é usarComponentDidUpdate()
eShouldComponentUpdate()
.ComponentDidUpdate()
é chamado sempre que o componente é atualizado e seShouldComponentUpdate()
retorna verdadeiro (ShouldComponentUpdate()
se não for definido, ele retornatrue
por padrão).shouldComponentUpdate(nextProps){ return nextProps.changedProp !== this.state.changedProp; } componentDidUpdate(props){ // Desired operations: ex setting state }
Esse mesmo comportamento pode ser realizado usando apenas o
ComponentDidUpdate()
método, incluindo a instrução condicional dentro dele.componentDidUpdate(prevProps){ if(prevProps.changedProp !== this.props.changedProp){ this.setState({ changedProp: this.props.changedProp }); } }
Se alguém tentar definir o estado sem uma condição ou sem definir,
ShouldComponentUpdate()
o componente irá renderizar novamentefonte
componentWillReceiveProps
está prestes a ser descontinuada e é sugerida contra o uso.Você poderia usar
KEY
uma chave única (combinação dos dados) que muda com os acessórios, e esse componente será renderizado novamente com os acessórios atualizados.fonte
componentWillReceiveProps(nextProps) { // your code here}
Acho que é o evento que você precisa.
componentWillReceiveProps
dispara sempre que seu componente recebe algo por meio de acessórios. De lá, você pode fazer sua verificação e fazer o que quiser.fonte
componentWillReceiveProps
obsoleto *Eu recomendaria dar uma olhada nesta resposta minha e ver se ela é relevante para o que você está fazendo. Se eu entendi seu verdadeiro problema, é que você simplesmente não está usando sua ação assíncrona corretamente e atualizando a "loja" redux, que irá atualizar automaticamente seu componente com seus novos adereços.
Esta seção do seu 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)) } }
Não deve ser acionado em um componente, deve ser tratado após a execução de sua primeira solicitação.
Dê uma olhada neste exemplo 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)) ); }; }
Você não precisa necessariamente usar redux-thunk, mas isso o ajudará a raciocinar sobre cenários como este e escrever um código correspondente.
fonte
makeASandwichWithSecretSauce
em seu componente?Um método amigável para usar é o seguinte, assim que o prop for atualizado, ele irá renderizar novamente o componente automaticamente:
render { let textWhenComponentUpdate = this.props.text return ( <View> <Text>{textWhenComponentUpdate}</Text> </View> ) }
fonte