React: componentDidReceiveProps Please

Criado em 27 fev. 2015  ·  94Comentários  ·  Fonte: facebook/react

Eu gostaria de solicitar humildemente um gancho componentDidReceiveProps, muitas vezes eu gostaria de fazer algo em componentWillMount e componentWillReceiveProps, mas porque this.props ainda não foi definido, eu sou forçado a passar adereços em vez de ler diretamente de this.props .

Antes do Novo Gancho

componentWillMount() {
  this.setup(this.props.id);
}

componentWillReceiveProps(next) {
  this.setup(next.id);
}

setup(id) {
  UserActions.load(id);
}

Depois de Novo Gancho

componentWillMount() {
  this.setup();
}

componentDidReceiveProps() {
  this.setup();
}

setup() {
  UserActions.load(this.props.id);
}

Neste exemplo simples, pode parecer uma coisa pequena, mas muitas vezes a passagem de adereços é profunda e, em vez de referenciar isso convenientemente. Adereços, é-se forçado a sondar os adereços em todo o componente.

Considere adicionar componentDidReceiveProps como um gancho para aproveitar o mesmo código que é aproveitado no componentWillMount sem forçar ambos a prender os suportes em todo o componente.

Comentários muito úteis

@syranide O problema é quando a configuração precisa chamar métodos que também precisam de adereços, que precisa chamar métodos que também precisam de adereços ... Eventualmente, todo o seu componente está girando em torno de adereços.

Todos 94 comentários

Por que não setup(props) ?

Olhando através dos meus próprios projetos, posso ver que fiz isso onde tinha necessidades semelhantes (outro está derivando um pedaço de estado com base em adereços), apenas passando em um conjunto diferente de adereços quando necessário, então você só precisa passar algo extra ao reutilizar, sem duplicar o conhecimento de quais adereços são necessários:

setup(props) {
  props = props || this.props
  UserActions.load(props.id)
}

@syranide O problema é quando a configuração precisa chamar métodos que também precisam de adereços, que precisa chamar métodos que também precisam de adereços ... Eventualmente, todo o seu componente está girando em torno de adereços.

+1 parece um monte de fiação desnecessária no aplicativo com o padrão atual. Pode ser uma forma padronizada e concisa de resolver o problema. Vi um monte de gente se queimar com this.props dentro de ComponentWillReceiveProps, um sinal claro de que não é intuitivo.

1, também acho isso frustrante.

Não acho mais que isso seja um problema.

+1, também estou passando muito por objetos. Semelhante ao @insin acima, eu estava usando parâmetros padrão por um tempo:

setup(props = this.props) {
  doSomething(props);
}

Mas decidi que era um antipadrão devido aos bugs sutis que pode causar se você esquecer de passar o newProps.

+1

Acho que não está disponível porque this.props e this.state _sempre_ correspondem a valores renderizados. Não há como implementar componentDidReceiveProps que não acione outra renderização sem quebrar essa restrição.

Na maioria das vezes, se você estiver usando componentWillReceiveProps , você realmente quer um componente de ordem superior a la Relay ou algo como observe gancho proposto para React 0.14 .

+1
Além disso, você pode implementar componentDidReceiveProps sem alterar this.props ou this.state . Se tudo o que você precisa fazer é ler a partir deles, você não acionará outra renderização. Se você está escrevendo para adereços ou estado dentro desta chamada de função proposta, então você está atirando no próprio pé, mas é o mesmo caso para todos os outros métodos no ciclo de vida.

+1

Quero ser capaz de responder ao novo evento de adereços quando shouldComponentUpdate retornar false , então, no meu caso, não posso usar componentDidUpdate .

+1

+1

E quanto a componentWillMount e componentWillUpdate @iammerrick

Quero ser capaz de responder ao novo evento de props quando shouldComponentUpdate retornar falso, portanto, no meu caso, não posso usar o componentDidUpdate.

Usar componentWillReceiveProps ?

+1

+1 isso ajuda com o código DRY e simplificando a lógica.

Eu estaria aberto para componentDidReceiveProps ser chamado na configuração inicial para tornar o exemplo que inicia este thread apenas uma única função:

componentDidReceiveProps() {
  UserActions.load(this.props.id);
}

Pensamentos?

componentWillReceiveProps() {
  setTimeout(()=> {
    if(this.isMounted()) {
      this.componentDidReceiveProps();
    }
  });
}
componentDidReceiveProps() {
  UserActions.load(this.props.id);
}

O que você acha?

@YourDeveloperFriend Acho que depende de como funciona em relação a outros ganchos de ciclo de vida e atrasos de renderização devido a tempos limite em cascata em componentes aninhados.

Esses tipos de ganchos de ciclo de vida devem ser chamados de forma síncrona em uma passagem que é garantida para ser chamada antes da renderização. Não estudei a base de código do React, mas estou assumindo que a renderização não é chamada em um tempo limite.

Provavelmente, a melhor solução será algo na linha de registrar ganchos de pré-renderização ou marcar componentes que tiveram seus props alterados para que a lógica de renderização chame componentDidReceiveProps antes de chamar render.

+1 por favor. Tão feio.

Nah, estou bem. Existem soluções melhores.

+1

componentDidReceiveProps() já existe: é chamado render() . No entanto, pode haver um caso (como no exemplo de @iammerrick ) onde você precisa carregar / acionar / etc algo antes de executar uma renderização. Isso significa uma e a única coisa: você fez algo errado.

Quando você faz coisas como

setup(id) {
    UserActions.load(id);
}

você introduz statefullness no componente ou (muito pior) fora. Por que você sempre precisa de load() dados toda vez que um componente recebe props (nem mesmo é garantido que sejam novos )? Se você fizer o carregamento lento, a maneira correta é solicitar os dados no método render() :

render() {
    var actions = UserActions.load(id);
    if (actions) // render
    else // show spinner or return null, etc.
}

UserActions.load = function(id) {
    if (data) return data;
    else // request the data, emit change on success
}

@robyoder , passar argumentos para funções não é feio. Pode parecer muito prolixo, mas é natural na linguagem de programação que você escolheu. Eliminar a API adicionando um método de ciclo de vida sobressalente apenas para reduzir o detalhamento é definitivamente feio.

Então me diga, @ me-andre, por que nós temos componentWillUpdate e componentWillReceiveProps ?

Na minha opinião, é porque eles têm finalidades diferentes e ambos são úteis. Assim como aqueles dois não são iguais, render não é o mesmo que o hipotético componentDidReceiveProps porque é chamado por outros motivos que não novos props; além disso, você não tem acesso aos adereços anteriores.

O benefício dos adereços anteriores é que você não teria que carregar dados todas as vezes; pode ser condicional com base nos adereços e se eles mudaram.

Obviamente, "[a feiura] está nos olhos de quem vê", porque adicionar um método de ciclo de vida adequado parece uma solução muito mais limpa para mim.

Talvez haja outra maneira de ver isso ...

Digamos que os objetivos principais aqui sejam:

(1) reduzir o erro humano - esqueci de passar os parâmetros ou usar props = props || this.props novamente
(2) para reduzir o código clichê que não agrega valor - por que escrever algo desnecessariamente
(3) para reduzir a carga cognitiva de tentar decidir quais métodos de ciclo de vida usar - por que preciso pensar sobre esse tipo de coisa, por que continuo usando abordagens ligeiramente diferentes, dependendo de como me sinto naquele dia, etc.

Portanto, talvez o espaço da solução gire em torno da simplificação do uso do React e da própria API React para atingir esses objetivos.

Se você pensar sobre o problema com esses objetivos em mente, talvez alguns métodos de ciclo de vida possam ser mesclados e se você estiver interessado em saber que é inicialização vs atualização, você pode saber pela assinatura / argumentos de chamada. Por exemplo: beforeRender(prevProps, prevState) ou update(prevProps, prevState) .

Pessoalmente, parece que há muitos métodos de ciclo de vida e se eles fossem chamados de forma mais consistente (sem ou com prevProps / prevState na aprovação e atualização inicial), isso poderia simplificar meu código e aumentar minha produtividade. Além disso, os nomes dos métodos de ciclo de vida são bem longos (eu tendo a copiá-los / colá-los ao invés de digitá-los) e é difícil lembrar quais existiram / existiram, o que me faz pensar que eles poderiam / deveriam ser simplificados.

@robyoder , a resposta curta porque temos componentWillUpdate e componentDidReceiveProps é que há uma grande diferença entre eles. Eles são semelhantes, é claro, mas principalmente por serem prefixados com componentWill .

| | estado mudou | componente não foi atualizado |
| --- | --- | --- |
| componentWillUpdate () | sim | não |
| componentWillReceiveProps () | não | sim |

Como você deve ter notado, existem pontos / condições do ciclo de vida em que um método é chamado e o outro não. É por isso que temos 2 métodos de ciclo de vida distintos.

No entanto, componentDidReceiveProps() conforme descrito neste tópico, não representa nenhum estado ou condição de componente e não dá acesso a nada que componentWillReceiveProps() não dê. Ele apenas adiciona um ligeiro açúcar sintático que pode ou não parecer útil ou mais fácil - esta é uma preferência pessoal, não um requisito técnico . E é exatamente por isso que não deve ser adicionado: é subjetivo. Você diz que passar argumentos é feio - mas também disse que "[a feiura] está nos olhos de quem vê". Para mim, parece que você respondeu a si mesmo.

@ me-andre, você pode estar respondendo para mim (@kmalakoff) e @robyoder ao mesmo tempo. Ou talvez não, mas vou aproveitar esta oportunidade para levar a discussão adiante ...

O que eu estava levantando é que talvez pensar acima da API atual com esses três objetivos acima pudesse fornecer novos insights ou perspectivas sobre como alguém pode simplificar a API para alcançá-los. Claro, depois de passar pelo exercício, podemos acabar no mesmo lugar.

Vamos fazer o exercício ...

Vamos supor que este tópico está sendo seguido e + 1 porque há algo importante nele e digamos que a adição de componentDidReceiveProps não seja uma boa ideia porque aumenta a área de superfície da API.

Tendo em mente a tabela e os 3 objetivos que apresentei, alguém tem alguma ideia para simplificar a API e / ou outra forma de dar às pessoas neste tópico o que elas desejam e não expandir a API (talvez até reduzindo / simplificando-a )?

@kmalakoff , os 3 pontos sobre os quais você fala estão conectados com a API apenas no sentido de que você usa a API quando os encontra. Eles não são causados ​​por um design ruim.

O primeiro problema sobre o qual você fala é que os métodos de ciclo de vida usam argumentos diferentes. Bem, isso é perfeitamente natural - eles servem a propósitos diferentes. componentWillReceiveProps aceita acessórios não porque a lua estava cheia quando este método foi adicionado - mas porque se trata de receber acessórios que ainda não foram atribuídos ao componente. props estão sendo passados ​​apenas em métodos onde eles (podem) diferir de this.props . Na verdade, isso é uma dica, não um problema.
O problema nº 3 é que é difícil para você decidir qual retorno de chamada / abordagem usar. Bem, isso não é realmente um problema até que os métodos que falei acima tenham a mesma assinatura. Depois de ter ambos state e this.state , props e this.props que são iguais (leia-se sem sentido) na maioria dos métodos, então você se enreda em opções sobressalentes e alternativas.
O problema nº 2 é sobre código clichê ... bem, React é uma biblioteca fina - e é bonita, fácil e restrita por causa disso. Se você deseja fazer um invólucro que simplifique nossas vidas e reduza a quantidade de código que escrevemos todos os dias - por que não fazê-lo? Publique-o como seu próprio npm que depende de react e pronto.

Em relação a "há muitos métodos de ciclo de vida" - ainda não e nunca será se você parar de solicitar novos retornos de chamada para os quais não tem uma necessidade técnica. A necessidade técnica é quando você implementa um componente complexo e no meio do trabalho você entende que não há absolutamente nenhuma maneira de fazer isso sem algum hack feio. É sobre "para acionar um método DOM complicado, preciso que algo seja chamado no momento em que o componente faz X, mas não existe tal método e não posso adicionar um, então eu uso setTimeout e espero que ele não pare antes do que eu preciso". Não quando você diz "oh, cansei de escrever adereços = this.props".

E mais uma coisa...
Em um aplicativo React bem projetado, você não precisa da maioria dos métodos de que falamos ou os usa muito raramente. getInitialState , componentWillMount , componentWillUnmount bastam 99% das vezes. Em meu projeto atual, que é um aplicativo comercial relativamente grande, componentWillReceiveProps é usado duas vezes em todo o aplicativo. Eu não o usaria, mas o ambiente (leia o navegador) é imperfeito - manipulando certas coisas "com estado em si", como scrollTop ou contando com cálculos de layout para render s futuros requer sincronização manual entre props transições e mudanças DOM. No entanto, na maioria dos casos "normais", você simplesmente não precisa saber quando a transição de props acontece.

@ me-andre com base na minha experiência de trabalho com React, acredito que há espaço para melhorar / simplificar a API. Não estou sozinho, por isso esse problema foi levantado em primeiro lugar e está sendo marcado com +1.

Se eu responder fornecendo feedback sobre sua análise, não acho que isso realmente vá avançar (é por isso que evitei fazer isso até agora), pois é um pouco tendencioso para o status quo e justifica a API atual, mas Terei todo o gosto em fornecer comentários sobre possíveis soluções! As pessoas envolvidas neste problema estão procurando explorar ideias de melhoria e possíveis mudanças de API para resolver os problemas levantados por meio de sua experiência com o React (como descrevi acima).

Talvez você esteja certo de que a comunidade deve fazer a inovação para mover a API para frente e talvez revertê-la para o núcleo ou talvez a API atual seja perfeita como está, mas acho que esta é uma grande oportunidade para considerar como a mudança pode parecer com a equipe React.

Talvez o motivo de você estar argumentando dessa forma seja porque somos apenas usuários da biblioteca com menos conhecimento profundo sobre a API atual e as decisões que foram tomadas. Talvez você possa trazer outros membros da equipe React que sejam tão experientes e bem informados quanto você para ver se temos uma perspectiva diferente e talvez até mesmo chegar a uma ótima solução ou para chegar a um consenso de que isso é tão bom quanto possível.

@kmalakoff , imagine que o React é um motor e 4 rodas. Agora, toda vez que você quer ir a algum lugar, você constrói o resto do carro e isso eventualmente começa a ser chato. Caso contrário, você reclama da necessidade de se lembrar de detalhes do motor de baixo nível, e girar as rodas dianteiras com as mãos não parece ser a maneira certa.
O que eu recomendaria é obter um veículo completo (uma estrutura completa) ou construir os componentes necessários de forma que você possa reutilizá-los ao longo do tempo.
O que vejo neste tópico são usuários de motor reclamando que o motor não tem sistema hidráulico e nenhuma luz interna. O que eu sinto é que teríamos uma porcaria completa se adicionarmos esses recursos onde eles não pertencem.
React não é um framework: é mais um motor de renderização com diff que expõe um poderoso sistema de componentes. Possui API minimalista e de baixo nível, mas poderosa o suficiente para permitir que você construa literalmente qualquer coisa sobre ela.
Por favor, não hesite em me contatar por e-mail se você sentir que preciso esclarecer algo - não quero que este tópico se torne um tutorial.

@ me-andre, acredito que agora entendemos bem sua posição e sua linha de argumentação. Obrigado! Você pode estar certo de que a API já é tão boa quanto possível, mas também existe a possibilidade de que ela possa ser melhorada.

Alguém pode defender a mudança da API? por exemplo. fornecendo uma análise comparativa de várias opções (adicionar componentDidReceiveProps vs mesclar / simplificar APIs vs nenhuma mudança)

A equipe do React tem observado o problema e nós o discutimos internamente. Em princípio, sempre tento eliminar a área de superfície da API, então me vejo inclinado para os argumentos de @me-andre. No entanto, nossa equipe às vezes se refere afetuosamente a componentDidReceiveProps como "o método de ciclo de vida ausente", e há discussões sérias sobre sua possível adição. O importante é que habilitemos as melhores práticas sem catalisar as más práticas.

O que seria mais útil para nós (ou pelo menos para mim) é ter um entendimento sólido de PORQUE as pessoas querem este método de ciclo de vida. O que você está tentando fazer neste ciclo de vida que não é suficientemente coberto pelos outros métodos de ciclo de vida? Por que alguém iria querer fazer UserActions.load(id); dentro de componentDidReceiveProps vez de renderizar como sugerido em https://github.com/facebook/react/issues/3279#issuecomment -163875454 (posso pensar em um motivo, mas estou curioso para saber quais são seus motivos)? Existem outros casos de uso além de iniciar um carregamento de dados com base em props?

@jimfb Acredito que componentDidReceiveProps é exatamente o que preciso e que outros métodos são inadequados, mas posso estar errado. Ficarei feliz em explicar meu caso de uso.

Eu tenho uma rota em meu react-router que se parece com esta:

    <Route path="/profile/:username" component={ProfilePage} />

Preciso buscar os dados para o perfil de uma fonte externa e, para fazer isso corretamente, preciso fazer uma solicitação HTTP no método componentDidMount .

Infelizmente, quando eu navego de um perfil para outro, o React Router não chama o método construtor ou o método componentDidMount novamente (é claro que isso pode ser um bug, mas presumo que não seja por agora) .

Pensei em consertar isso usando o componentDidReceiveProps teórico. Infelizmente, ele ainda não existe e componentWillReceiveProps não atenderá às minhas necessidades.

Quaisquer sugestões sobre isso seriam muito apreciadas.

Meu palpite é que componentDidReceiveProps é exatamente o que eu preciso.

@ shea256 Você pode explicar por que componentWillReceiveProps não atende às suas necessidades?

@ shea256 Além disso, por que seu componente precisa fazer uma solicitação HTTP? O que essa solicitação http contém? Se dados, por que você não está atualizando seu componente de forma assíncrona quando o retorno de chamada de dados retorna?

@jimfb O caso comum para mim é quando temos que carregar algo de forma assíncrona em resposta a uma mudança de prop, o que envolve definir algum estado para indicar que o carregamento está acontecendo e, em seguida, quando os dados são carregados, definir esse estado.

Tanto a montagem quanto o recebimento de novos adereços devem acionar o mesmo ciclo de carregamento, então componentDidMount e componentWillReceiveProps é o lugar para chamar a função de atualização. render não tem informações sobre se é um novo prop e, de qualquer forma, setState do render é proibido.

Portanto, tenho uma função que faz o carregamento. Mas eu tenho que passar props como um parâmetro e cuidadosamente evitar usar this.props que estão desatualizados para componentWillReceiveProps . Isso inevitavelmente acaba causando bugs, pois você tem que se lembrar de usar os adereços passados ​​ou você obterá erros sutis quando as mudanças chegarem. E as assinaturas de todos os métodos envolvidos são mais complexas, pois os adereços precisam ser passados.

Sim, isso pode ser feito com a API atual. Mas isso causa um código mais desajeitado e sujeito a erros, que é o que o React é tão bom em evitar em geral.

@jimfb Preciso obter o novo nome de usuário em routeParam e com componentWillReceiveProps ainda não o tenho.

Preciso fazer uma solicitação HTTP para carregar os dados do perfil de uma fonte externa (os dados não são armazenados localmente).

E posso atualizar meu componente no método de renderização, mas parece um pouco sujo:

constructor(props) {
  super(props)

  this.state = {
    id: this.props.routeParams.id,
    profile: {}
  }
}

componentDidMount() {
  this.getProfile(this.state.id)
}

render() {
  if (this.props.routeParams.id !== this.state.id) {
    this.getProfile(this.props.routeParams.id)
  }

  return (
    <div>
      <div className="name">
       {this.state.profile.name}
      </div>
    </div>
  )
}

@grassick escreveu:

O caso comum para mim é quando temos que carregar algo de forma assíncrona em resposta a uma mudança de prop, o que envolve definir algum estado para indicar que o carregamento está acontecendo e, em seguida, quando os dados são carregados, definir esse estado.

Sim, este é exatamente meu caso de uso.

Tanto a montagem quanto o recebimento de novos adereços devem acionar esse mesmo ciclo de carregamento, portanto, componentDidMount e componentWillReceiveProps é o lugar para chamar a função de atualização.

Bem dito.

@ shea256 isso não pode ser feito usando componentWillReceiveProps vez disso?

constructor(props) {
  super(props)

  this.state = {
    id: this.props.routeParams.id,
    profile: {}
  }
}

componentDidMount() {
  this.getProfile(this.state.id)
}

componentWillReceiveProps(nextProps) {
  let { id } = nextProps.params
  if(id !== this.state.id) {
    this.getProfile(id, (profile) => {
      this.setState({ id: id, profile: profile })
    })
  }
}

render() {
  return (
    <div>
      <div className="name">
       {this.state.profile.name}
      </div>
    </div>
  )
}

@grassick escreveu:

O caso comum para mim é quando temos que carregar algo de forma assíncrona em resposta a uma mudança de prop, o que envolve definir algum estado para indicar que o carregamento está acontecendo e, em seguida, quando os dados são carregados, definir esse estado.

@grassick escreveu:

render não tem informações sobre se é um novo prop e, de qualquer forma, setState de render é um não-não.

Apenas cuspindo aqui: se componentDidUpdate fosse invocado na renderização inicial (além das renderizações subsequentes), isso resolveria seu caso de uso? Você poderia verificar se os adereços mudaram e carregar todos os seus dados em componentDidUpdate , certo? E na renderização, você saberia que estava carregando dados se this.state.profileName != this.props.profileName . Essa alternativa seria suficiente para seus casos de uso?


Existem casos de uso que as pessoas têm que NÃO envolvem componentes que carregam dados com base em adereços?

@calmdev hm, acredito que você tenha razão. Vou tentar isso.

@jimfb talvez, embora eu tenha tentado usar componentDidUpdate e tenha achado que não funcionou. Eu posso dar outra olhada.

E parece que eu poderia fazer uma verificação em this.state.profileName != this.props.profileName mas isso também parece um hack, não é? Nesse ponto, se componentWillReceiveProps(nextProps) acabar funcionando, eu prefiro assim. Agora, o que me incomoda é a falta de simetria com componentDidMount . Posso usar componentWillMount em vez de componentDidMount ?

Só sinto que todo o ciclo de vida poderia ser mais limpo.

@ shea256 , tenho uma pergunta para você ... você leu README para React?
Não me sinto confortável em dizer isso, mas se não, você provavelmente não deve solicitar novos recursos para a ferramenta com a qual não está familiarizado. Para mim, isso até parece ... absurdo.
"Posso usar componentWillMount em vez de componentDidMount ?"
Não, você não pode porque, conforme declarado no leia-me, componentWillMount é chamado antes que seu componente chegue ao DOM e componentDidMount - depois disso. Bem, você definitivamente pode substituir um método por outro e isso apenas quebraria seu código. A razão de termos 2 métodos aqui não é estética (leia "simetria"). É porque podemos precisar de um método para fazer uma preparação antes de renderizar e o outro para alguma consulta / manipulação DOM inicial após a primeira renderização. Mas mesmo isso é exótico para um componente React médio. Normalmente, você simplesmente não precisa acessar o DOM manualmente.
"E parece que eu poderia fazer uma verificação completa em this.state.profileName! = This.props.profileName, mas isso também parece um hack, não é?"
Sim, toda a sua abordagem é um hack. E quem lhe disse que componentDidReceiveProps garantiria que props foram alterados? Nada jamais fará isso a menos que você defina shouldComponentUpdate .
É assim que o React funciona.

@ me-andre, obrigado por intervir, mas você está sendo um pouco abrasivo para o meu gosto. Além disso, não acredito que você tenha entendido minhas perguntas. Estou bastante familiarizado com o que componentWillMount e componentDidMount fazem. Vou esperar a resposta de @jimfb .

@ shea256 ,
"Também não acredito que você entendeu minhas perguntas."
Você poderia, por favor, apontar onde eu perco o ponto ao responder às suas perguntas?
Além disso, você poderia esclarecer o que está tentando perguntar.
Além disso, não estamos aqui para discutir personalidades ou gostos. Esta não é uma conversa privada, nem é um suporte técnico. Este não é nem mesmo um site de troca de pilha onde você poderia esperar um para guiá-lo através de alguma área de conhecimento.
Costumo falar em encontros locais e também em conferências internacionais sobre React (e não apenas) e estou muito aberto para compartilhar conhecimentos - quando e onde for apropriado. Você sempre pode entrar em contato comigo diretamente por e-mail ou skype.

Em relação ao seu problema com o carregamento de dados de perfil. Você está tentando resolver o problema de aplicar a abordagem imperativa clássica a uma estrutura orientada a funções. Uma visualização no React é uma função de adereços, estado e ambiente. Como function(state, props) { return // whatever you've computed from it } (mas as coisas ficam um pouco mais complexas no mundo real - caso contrário, o React não existiria). Embora em 0.14 tenhamos componentes funcionais puros, para a maioria dos componentes esta função de entrada é render() .

Significa que você começa a escrever a partir de render() e assume que seus adereços estão sempre atualizados e não se importa se props foi alterado ou não, quantas vezes e onde . Seu caso poderia ser implementado da seguinte maneira:

// profile-view.js

var React = require('react');

module.exports = React.createClass({
    contextTypes: {
        app: React.PropTypes.object.isRequired
        // another option is var app = require('app')
        // or even var profiles = require('stores/profiles')
    },
    componentWillMount() {
        var {app} = this.context; // another option is to require('app')
        app.profiles.addListener('change', this.onStoreChange);
    },
    componentWillUnmount() {
        var {app} = this.context; // another option is to require('app')
        app.profiles.removeChangeListener('change', this.onStoreChange);
    },
    onStoreChange() {
        this.forceUpdate();
    },
    render() {
        var {app} = this.context;
        var profile = app.profiles.read(this.props.routeParams.id);
        if (profile) { // profile's been loaded
            return <div>{profile.name}</div>;
        } else { // not yet
            return <div>Loading...</div>;
        }
    }
});

// profiles-store.js
// app.profiles = new Profiles() on app initialization

var EventEmitter = require('events');
var {inherits} = require('util');

module.exports = Profiles;

function Profiles() {
    this.profiles = {};
}

inherits(Profiles, EventEmitter);

Profiles.prototype.read = function(id) {
    var profile = this.profiles[id];
    if (!profile) {
        $.get(`profiles/${id}`).then(profile => {
            this.profiles[id] = profile;
            this.emit('change');
        });
    }
    return profile;
};

Bem simples.
E você não precisa de componentDidReceiveProps . Você nem mesmo precisa de componentWillReceiveProps e ganchos semelhantes. Se você sentir que precisa deles para um caso trivial, está perdendo o entendimento de como fazer as coisas no React. Para obtê-lo , use os recursos apropriados, não apenas destrua a seção Problemas do repositório . É um pouco abrasivo demais para o meu gosto. Parece até que você não respeita o tempo dos outros.

@ me-andre Provavelmente vale a pena abrandar um pouco o tom da sua linguagem, pois pode parecer um pouco conflituoso, mesmo que você esteja apenas tentando ajudar. Queremos criar uma comunidade acolhedora para todos; todos nós éramos novatos em um ponto. Mesmo que você esteja correto sobre alguns de seus pontos sobre a API / design, o próprio fato de tantas pessoas estarem marcando o problema com +1 é uma indicação de que algo está errado, então devemos tentar entender o que / por que as pessoas querem isso vida útil. Talvez o problema seja que não estamos comunicando suficientemente como escrever componentes (documentação) de forma adequada, ou talvez a API do React precise de uma mudança - de qualquer forma, vale a pena entender as reclamações / perguntas / comentários aqui.

@ shea256 this.state.profileName != this.props.profileName está verificando se o estado interno (o que o componente está renderizando) corresponde ao que o pai está pedindo ao componente para renderizar. Esta é quase a definição de "componente está atualizando" ou "componente está atualizado", então eu não vejo isso como um hacky. Pelo menos, não é mais hacky do que a ideia de "disparar uma solicitação de atualização de dados quando o prop muda e fazer um setstate no retorno de chamada" está em primeiro lugar.

@ shea256 Para ser claro: esse ciclo de vida proposto não permitiria que você fizesse nada que já não pudesse ser feito hoje com os ciclos de vida atuais. Isso apenas o tornaria potencialmente mais conveniente. Como outros mencionaram, o que você está tentando fazer é possível com os ciclos de vida atuais (existem várias combinações de ciclos de vida que permitiriam que você atingisse seu objetivo). Se você está tentando "fazer funcionar", StackOverflow seria um lugar melhor para discutir. Este problema do github está tentando entender a necessidade de componentDidReceiveProps no que se refere à ergonomia do desenvolvedor.

E quem disse que componentDidReceiveProps garantiria que os adereços foram alterados?

@ me-andre está correto aqui. A maioria dos métodos de ciclo de vida não garante realmente que algo mudou; eles apenas indicam que algo _pode_ ter mudado. Por esse motivo, você sempre precisa verificar se previous === next se vai fazer algo como fazer uma solicitação http. Muitas pessoas neste segmento parecem estar fazendo a suposição de que seu valor mudou simplesmente porque o método de ciclo de vida foi disparado.

@ me-andre escreveu:

Você está tentando resolver o problema de aplicar a abordagem imperativa clássica a uma estrutura orientada a funções.

Acho que essa pode ser a causa raiz das pessoas que desejam esse novo método de ciclo de vida, mas ainda estou tentando descobrir por que / como as pessoas querem usar esse método. É inteiramente possível que a semântica do ciclo de vida atual esteja um pouco mal alinhada com o que as pessoas geralmente desejam fazer.

Todos: há algum caso de uso para componentDidReceiveProps além de "carregar dados de forma assíncrona em resposta a uma mudança de prop"?

cc @grassick @iammerrick

@jimfb Pesquisei meu código e também uso componentWillReceiveProps para construir objetos caros de criar que são necessários para renderização. Este caso sofre do mesmo problema que tem que passar props e ter cuidado para não usar this.props no código.

@jimfb escreveu:

@ me-andre Provavelmente vale a pena abrandar um pouco o tom da sua linguagem, pois pode parecer um pouco conflituoso, mesmo que você esteja apenas tentando ajudar. Queremos criar uma comunidade acolhedora para todos; todos nós éramos novatos em um ponto. Mesmo que você esteja correto sobre alguns de seus pontos sobre a API / design, o próprio fato de tantas pessoas estarem marcando o problema com +1 é uma indicação de que algo está errado, então devemos tentar entender o que / por que as pessoas querem isso vida útil. Talvez o problema seja que não estamos comunicando suficientemente como escrever componentes (documentação) de forma adequada, ou talvez a API do React precise de uma mudança - de qualquer forma, vale a pena entender as reclamações / perguntas / comentários aqui.

Obrigado por mencionar isso. É muito importante ter certeza de que você tem uma comunidade acolhedora e minhas comunicações com você têm sido agradáveis.

@jimfb escreveu:

@ shea256 this.state.profileName! = this.props.profileName está verificando se o estado interno (o que o componente está renderizando) corresponde ao que o pai está pedindo ao componente para renderizar. Esta é quase a definição de "componente está atualizando" ou "componente está atualizado", então eu não vejo isso como um hacky. Pelo menos, não é mais hacky do que a ideia de "disparar uma solicitação de atualização de dados quando o prop muda e fazer um setstate no retorno de chamada" está em primeiro lugar.

Sim, isso parece razoável.

@jimfb escreveu:

@ shea256 Para ser claro: esse ciclo de vida proposto não permitiria que você fizesse nada que já não pudesse ser feito hoje com os ciclos de vida atuais. Isso apenas o tornaria potencialmente mais conveniente. Como outros mencionaram, o que você está tentando fazer é possível com os ciclos de vida atuais (existem várias combinações de ciclos de vida que permitiriam que você atingisse seu objetivo). Se você está tentando "fazer funcionar", StackOverflow seria um lugar melhor para discutir. Este problema do github está tentando entender a necessidade de componentDidReceiveProps no que se refere à ergonomia do desenvolvedor.

Eu concordo muito com isso. Não postei meu caso de uso específico para receber ajuda sobre ele. Publiquei para fornecer algumas dicas sobre por que pensei que componentDidReceiveProps seria útil.

Eu e mais de uma dúzia de outras pessoas que postaram claramente tínhamos um instinto para usar algo assim (o que significa que provavelmente existem centenas ou milhares mais), o que me indica que a API não é tão intuitiva quanto poderia ser.

@jimfb escreveu:

@ me-andre está correto aqui. A maioria dos métodos de ciclo de vida não garante realmente que algo mudou; eles apenas indicam que algo pode ter mudado. Por esta razão, você sempre precisa verificar se anterior === próximo se for fazer algo como fazer uma solicitação http. Muitas pessoas neste segmento parecem estar fazendo a suposição de que seu valor mudou simplesmente porque o método de ciclo de vida foi disparado.

Não estou fazendo essa suposição. Eu deixei o cheque de fora, mas agora estou usando um. Mas, na pior das hipóteses, uma solicitação adicional seria acionada.

@jimfb escreveu:

Acho que essa pode ser a causa raiz das pessoas que desejam esse novo método de ciclo de vida, mas ainda estou tentando descobrir por que / como as pessoas querem usar esse método. É inteiramente possível que a semântica do ciclo de vida atual esteja um pouco mal alinhada com o que as pessoas geralmente desejam fazer.

Possivelmente. Vou pensar mais sobre isso.

componentDidReceiveProps seria útil para um caso de uso específico que eu tenho.

Estou usando a arquitetura ReactRouter e Flux. Quando meu componente é instanciado, defino meu estado para um item de uma loja. Quando aquela loja emite um evento de mudança, eu atualizo meu estado usando a mesma consulta de recuperação.

Quando meus adereços mudam porque eu naveguei para um ID de item diferente na mesma rota, preciso consultar minha loja novamente ou meu componente ficará preso com o estado do item anterior na loja.

Atualmente, quando componentWillReceiveProps é chamado, verifico se algum dos meus parâmetros de rota mudou e, se mudou, chamo updateItemState. Mas, como os adereços ainda não mudaram, agora devo passar os adereços para o meu método.

this.updateItemState( nextProps );

updateItemState( props ) {
    props = props || this.props;

    this.setState( {
        item: this.getItemState( props )
    } );
}

getItemState( props ) {
    props = props || this.props;

    return this.ItemStore.get( props.params[ this.paramName ] );
}

simplesmente se tornaria

this.updateItemState();

updateItemState() {
    this.setState( {
        item: this.getItemState()
    } );
}

getItemState() {
    return this.ItemStore.get( this.props.params[ this.paramName ] );
}

Acho que este é um caso de uso razoável.

@ akinnee-gl et al: Se componentDidUpdate fosse disparado após a montagem inicial, além das atualizações, isso resolveria seu caso de uso?

@ akinnee-gl, se você usa o Flux, não precisa definir o estado de uma loja. A menos que seja absolutamente impossível, você deve manter o estado em um só lugar. Quando se trata de Flux, esse lugar é a própria loja. Quando a loja emite troco, você apenas forceUpdate() e, em seguida, em render() você read() sua loja.

"Quando meus adereços mudam porque eu naveguei para um ID de item diferente na mesma rota, preciso consultar minha loja novamente ou meu componente ficará preso com o estado do item anterior na loja."

Nunca.

Dê uma olhada: você precisa renderizar um item específico de uma loja e qual item renderizar você decide de props . Se for esse o seu requisito, deve ser expresso no código da mesma forma que em palavras:

    var item = this.props.store.read(this.props.id);
    return <div>{item.name}</div>;

Este é o seu componente, nada mais.
Para fazer esse jogo com o Flux, você pode escrever um componente reutilizável para vinculação / desvinculação de loja:

<FluxWrapper store={store} component={Component} id={this.props.routeParams.id} />

var FluxWrapper = React.createClass({
    componentWillMount() {
        this.props.store.addListener('change', this.onStoreChange);
    },
    componentWillUnmount() {
        this.props.store.removeListener('change', this.onStoreChange);
    },
    onStoreChange() {
        this.forceUpdate();
    },
    render() {
        var Component = this.props.component;
        return <Component {...this.props} />;
    }
});

var Component = React.createClass({
    render() {
        var item = this.props.store.read(this.props.id);
        return <div>{item.name}</div>;
    }
});

Veja como seus componentes podem se tornar minúsculos se você usar o React corretamente.

No entanto, se suas lojas são pesadas e você não pode apenas read() nelas em cada render() , então você precisa de um middleware de cache. Isso pode ser um componente reutilizável (semelhante a FluxWrapper ) ou um armazenamento proxy intermediário que obscurece o método read() . Os armazenamentos proxy são fáceis e interessantes - eles podem não apenas armazenar em cache as leituras, mas também suprimir alterações se uma alteração no armazenamento pai não for importante ou significativa para o caso.
Isso é chamado de composição. Você deve preferir a composição à herança ou estender a funcionalidade ou qualquer outra coisa, porque a composição é dimensionada .
Assim que você tiver um problema que seja difícil de resolver com o uso de um componente, considere o uso de 2 ou mais componentes em vez de introduzir complexidade aos componentes existentes.

@jimfb , em relação ao meu tom: Não sou um falante nativo de inglês e nem mesmo tenho uma prática regular de falar, então às vezes posso escolher as palavras erradas - eu simplesmente não sinto como elas soam emocionalmente.

Abordagem interessante. Vou manter isso em mente. Todos os documentos oficiais do React dizem para manter suas funções de renderização puras (o que significa que você só deve acessar props e estado nos métodos de renderização). Todos os exemplos de Flux mostram o estado de configuração da loja.

@jimfb O problema com o componentDidUpdate é que ele é chamado por outro motivo que não a mudança de props. Além disso, seria necessário esperar até que o componente já estivesse atualizado e, em seguida, chamar setState para atualizá-lo novamente! Parece muito ineficiente.

@ akinnee-gl componentWillReceiveProps também pode ser chamado por outros motivos que não a alteração dos adereços (consulte: https://github.com/facebook/react/issues/3279#issuecomment-164713518), então você DEVE verificar para ver se o prop realmente mudou de qualquer maneira (independentemente do ciclo de vida que você escolher). Além disso, se você estiver realmente fazendo dados assíncronos, estará esperando pelo próximo loop de evento antes que seu retorno de chamada seja disparado de qualquer maneira, então não acho que importaria se sua função fosse invocada imediatamente antes / depois de seu função de render, certo?

@jimfb Bem, na verdade, se o item que o objeto está fazendo referência já estiver na loja, não há necessidade de renderizar novamente. Se ainda não estiver na loja, podemos mostrar uma tela de carregamento. Portanto, ainda não faz sentido renderizar duas vezes. Eu vejo o que você está dizendo, no entanto.

@ me-andre Eu realmente gosto da sua abordagem de apenas ler da loja durante a renderização. Parece simplificar o código. Eu me preocupo se você perder alguma eficiência ao fazer isso. Você perde a capacidade de controlar a atualização por meio de shouldComponentUpdate, e isso requer que você adicione aquele componente FluxWrapper extra que você mencionou. Alguma opinião sobre isso?

Tenho monitorado a discussão e gostaria de ressurgir da terceira opção: (1) API está ótima como está, (2) ela precisa de componentDidReceiveProps e (3) talvez haja uma API mais simples.

Estou olhando para o problema como uma oportunidade de ver se podemos resolver a necessidade subjacente levantada por esse problema por meio de um brainstorming de mudanças mais profundas na API, em vez de limitar a discussão ao espaço de solução de (1) e (2).

Uma API simplificada pode ser baseada nos seguintes princípios:

(A) simplificar a estrutura do código / reduzir o boilerplate
(B) manter os suportes (entradas) separados do estado (interno)

O principal motivo para (A) é que me vejo escrevendo um texto clichê que não agrega valor. Exemplo:

componentWillMount() { this.actuallyCheckThePropsAndMaybeDoSomething(); }
componentWillReceiveProps(nextProps) { this.actuallyCheckThePropsAndMaybeDoSomething(nextProps); }

actuallyCheckThePropsAndMaybeDoSomething(props) {
  props = props || this.props;

  let relatedProps1 = _.pick(props, KEYS1);
  if (!shallowEqual(this.relatedProps1, relatedProps1) { // changed
   this.relatedProps1 = relatedProps1;

   // do something
  }

  let relatedProps2 = _.pick(props, KEYS2);
  if (!shallowEqual(this.relatedProps1, relatedProps2) { // changed
   this.relatedProps2 = relatedProps2;

   // do something else
  }
}

Eu realmente preferiria fazer o seguinte e não ter um caminho de código separado para a primeira vez em vez de alterado:

propsUpdated(prevProps) {
  if (!shallowEqual(_.pick(prevProps || {}, KEYS1), _.pick(this.props, KEYS1)) { // changed
   // do something
  }

  if (!shallowEqual(_.pick(prevProps || {}, KEYS2), _.pick(this.props, KEYS2)) { // changed
   // do something
  }
}

Quanto a (B), o principal problema com o componentDidUpdate é como acabamos de mencionar é que você pode disparar chamadas de método repetidas se definir o estado devido a alterações de propriedade, uma vez que é chamado tanto para props (entradas) quanto para estado (interno). Esses caminhos de código parecem ser melhores como desacoplados porque os adereços são fornecidos de fora e o estado é definido de dentro, por exemplo. Eu tentei / considerei uma quarta possibilidade de updated(prevProps, prevState) (um nome simplificado para componentDidUpdate já que menos métodos de ciclo de vida poderiam permitir uma nomenclatura mais curta) usando componentDidUpdate para reduzir o boilerplate, mas estou um pouco insatisfeito com o segundo potencial redundante atualizar chamadas e que a lógica parece ser bastante independente na prática.

Com base nos princípios de design (tenho certeza de que existem mais!), Estou me perguntando se algo ao longo das linhas de propsUpdated e stateUpdated poderia ser uma terceira opção potencial para esta discussão ?

@kmalakoff Você sempre precisará verificar se os adereços mudaram, já que nunca seremos capazes de dizer definitivamente que os adereços mudaram / não mudaram (porque Javascript não tem tipos de valor e tem mutabilidade).

Por falar nisso, talvez ele pudesse chamar shouldComponentUpdate para dizer se deveria chamar propsDidChange ou qualquer outra coisa. No entanto, assunto separado.

@jimfb não segue exatamente qual é a limitação de linguagem à qual você está se referindo e como ela se relaciona com a simplificação da API em geral. Você pode explicar um pouco mais detalhadamente ou com um exemplo? Preciso dessa explicação um pouco mais simplificada / ampliada para entender ...

Eu acho que ele quis dizer que não há uma maneira rápida embutida de verificar se this.props === nextProps porque eles podem ou não ser dois objetos separados.

{ a: 1 } === { a: 1 } produz falso porque são dois objetos separados, mas

var a = { a: 1 };
var b = a;
a === b

retorna true porque eles são, na verdade, uma referência ao mesmo objeto.

Poderíamos verificar recursivamente a igualdade de cada propriedade, mas isso poderia ser muito lento. É por isso que cabe a nós implementar shouldComponentUpdate

@kmalakoff Não quero divagar este tópico, mas aqui está uma essência para você: https://gist.github.com/jimfb/9ef9b46741efbb949744

TLDR: @ akinnee-gl está exatamente correto em sua explicação (Obrigado!). Com a pequena correção de que nem sempre poderíamos fazer a verificação recursiva de qualquer maneira (mesmo se quiséssemos, e o desempenho não fosse um problema), porque não há como verificar recursivamente uma função de retorno de chamada passada como um prop.

Vamos manter este tópico no tópico: P.

Obrigado a ambos! Hmmm, ainda não está totalmente claro por que simplificar a API não é uma opção para resolver esse problema. Vou adicionar comentários à essência ...

Se alguém quiser participar de uma gama mais ampla de soluções, junte-se a nós lá!

@jimfb @calmdev Tentei suas sugestões e agora tenho uma compreensão completa de por que componentDidReceiveProps não adiciona nada de novo e não deve ser adicionado como uma função adicional. Embora eu tenha confiado em sua palavra antes, agora faz sentido intuitivo para mim por que isso acontece.

Para ilustrar como cheguei a essa conclusão, gostaria de compartilhar meu código mais recente. Talvez isso possa ajudar outras pessoas também.

Dê uma olhada no meu componente da página de perfil:

class ProfilePage extends Component {
  static propTypes = {
    fetchCurrentProfile: PropTypes.func.isRequired,
    currentProfile: PropTypes.object.isRequired
  }

  constructor(props) {
    super(props)
    this.state = { currentProfile: {} }
  }

  componentHasNewRouteParams(routeParams) {
    this.props.fetchCurrentProfile(routeParams.id)
  }

  componentWillMount() {
    this.componentHasNewRouteParams(this.props.routeParams)
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.routeParams.id !== this.props.routeParams.id) {
      this.componentHasNewRouteParams(nextProps.routeParams)
    }
    this.setState({
      currentProfile: nextProps.currentProfile
    })
  }

  render() {
    var profile = this.state.currentProfile
    return (
      <div>
        <h1>{ profile.name ? profile.name : null }</h1>
      </div>
    )
  }
}

Quando o componente é montado, ele não possui dados de perfil e precisa carregar o perfil de uma fonte externa.

Então ... peço que chame a função fetchCurrentProfile (envolvida em componentHasNewIdProp ) que busca os dados da fonte externa e, em seguida, despacha uma ação de atualização para Redux. O redutor recebe essa ação e atualiza o estado do aplicativo, que então atualiza os adereços para o componente ProfilePage .

Agora, como os adereços foram atualizados, a função componentWillReceiveProps é chamada. Mas não sabemos o contexto do evento de mudança de adereços, então precisamos descobrir quais adereços (se houver) foram alterados. Vemos que o ID é o mesmo, então não precisamos chamar fetchCurrentProfile novamente. Em seguida, atualizamos state.currentProfile para que o componente saiba como renderizar novamente com os novos dados de perfil (sim, eu poderia fazer isso com adereços, mas há outro motivo para isso que não quero entrar aqui). Neste momento, poderíamos verificar se props.currentProfile mudou antes de atualizar o estado, mas esta não é uma operação cara, então não importa se verificarmos.

Então ... digamos que queremos navegar de um perfil para outro. Clicamos em um link e o roteador aciona a mudança de rota. Agora temos novos parâmetros de rota e a função componentWillReceiveProps é acionada. Vemos que id mudou e então chamamos fetchCurrentProfile novamente (via componentHasNewIdProp ).

Quando a nova busca volta e despacha a ação redux, o currentProfile prop é atualizado novamente, o estado é então atualizado e o componente é renderizado novamente com a nova visão.

Este exemplo ilustra como deve haver um comportamento diferente quando o componente é montado e quando o componente recebe novos adereços. Ele também demonstra como os adereços devem ser verificados para mudanças no último caso e como certas funções devem ser chamadas apenas em certas mudanças de prop.

Olhando para trás, essencialmente o que eu queria era uma função que só fosse atualizada quando o routeParams mudasse. No entanto, não há necessidade de adicionar outra função porque podemos fazer tudo o que quisermos com as funções existentes, além de não querer complicar o ciclo de vida.

Algo que pode ajudar aqui, porém, é mais documentação sobre o ciclo de vida, exemplos de trabalho, etc.

Obrigado pela ajuda pessoal. Por favor, deixe-me saber se há algo errado aqui. Espero que isso seja útil para quem se depara com este tópico no futuro.

@ shea256, seu exemplo parece um caso razoável para não precisar de uma nova API para conseguir o que você precisa sem um novo ponto de vista da API.

Descrevi mais pensamentos em: https://gist.github.com/jimfb/9ef9b46741efbb949744 , mas ainda sinto que devemos ser capazes de otimizar a API para escrever menos código ...

Imagine que a API permitiu que você escrevesse a mesma funcionalidade, mas com menos clichê:

class ProfilePage extends Component {
  static propTypes = {
    fetchCurrentProfile: PropTypes.func.isRequired,
    currentProfile: PropTypes.object.isRequired
  }
  state = {currentProfile: {}}; // not material to example (babel-stage-1)

  // called both on init and update
  willReceiveProps(nextProps) {
    if (!this.props || (this.props.routeParams.id !== nextProps.routeParams.id)
      nextProps.fetchCurrentProfile(nextProps.routeParams.id)
  }

  render() {
    var profile = this.props.currentProfile;
    return (
      <div>
        <h1>{ profile.name ? profile.name : null }</h1>
      </div>
    )
  }
}

A ideia que espero ser considerada é reduzir a área de superfície da API para diminuir o número de caminhos de código e clichês. Estou me perguntando se a discussão está ancorada na solicitação inicial de adicionar algo, quando talvez olhar para isso de uma perspectiva diferente abriria novos caminhos.

Esta é apenas uma opção (minha preferência é receivedProps (prevProps) porque idealmente, haveria apenas um método relacionado a prop onde você poderia usar this.props), mas a parte importante é que há um caminho de código para a inicialização do componente e quaisquer alterações de prop, em vez dos diferentes caminhos de código que temos agora com diferentes assinaturas de API. Qualquer que seja a solução, as pessoas concordam em ser melhores.

Conseguir uma redução no padrão é o que espero que este problema possa alcançar (para esclarecer minha necessidade / problema!) E gostaria de fazer um brainstorm um pouco mais amplo sobre como essa solução poderia ser (considere remover APIs, alterar o métodos de ciclo de vida significativamente, encurtando os nomes dos métodos agora que o ES6 está amplamente disponível e os componentes parecem ser o caminho a seguir, etc).

@ akinnee-gl, ter um aplicativo composto de muitos componentes minúsculos é muito melhor do que alguns componentes pesados ​​por vários motivos:

  1. Esta abordagem se conecta com o "princípio de responsabilidade única"
  2. Você pode ver claramente qual componente faz o que
  3. Você pode facilmente compor recursos e estender componentes envolvendo
  4. Métodos e propriedades não colidem
  5. É mais fácil estender um recurso porque um componente encapsula um recurso

Com relação ao seu aviso de que a leitura de uma loja em cada renderização pode ser ineficiente, fiz uma ideia geral que mostra uma ideia básica de como implementar um componente de middleware para armazenamento em cache: https://gist.github.com/me-andre/0ca8b80239cd4624e6fc

Eu fiz outra avaliação dos princípios / objetivos que poderiam levar a uma API aprimorada (esperançosamente com base em um método de adereços recebidos, conforme sugerido nesta edição):

(A) simplificar a estrutura do código / reduzir o boilerplate
(B) manter os suportes (entradas) separados do estado (interno)
(C) permitir um método para controlar a renderização de um componente
(D) simplificar a nomenclatura do método com base na suposição de subclasses de componentes
(E) reduzir o número de métodos de adereços para um para inicialização e mudanças
(F) o método props deve ter this.props com o valor mais recente

Existem esclarecimentos sobre alguns deles na essência.

@kmalakoff De acordo com a essência, não vamos aumentar o escopo desses problemas. O problema já é complexo o suficiente, e desviar a conversa para um redesenho completo da API não será tratável. Você é bem-vindo para continuar pensando sobre essas questões, mas mantenha este tópico no tópico.

@kmalakoff Muitos dos pontos que você https://github.com/reactjs/react-future/issues/40#issuecomment -142442124 e https://github.com/reactjs/react-future/issues/40#issuecomment - 153440651. Esses são os tópicos apropriados para ter essa discussão. Se você estiver procurando por discussões mais gerais / holísticas de design / API, a mídia correta provavelmente é https://discuss.reactjs.org/

@jimfb Pensei um pouco sobre isso depois que você sugeriu que eu movesse isso para a essência e, na verdade, não acredito que essa linha de análise esteja fora do assunto ... o motivo pelo qual estou interessado nesta questão sobre adereços recebidos é que acredito ter um método de ciclo de vida de adereços recebidos me permitirá reduzir o clichê.

Por favor, me ouça ... reduzir o clichê é o problema que o método de ciclo de vida nesta edição poderia resolver para mim.

Eu acredito que também pode ser uma parte significativa do motivo pelo qual as pessoas estão interessadas neste método de ciclo de vida, porque a API atual incentiva o clichê mais do que gostaríamos.

Dito isso, ficaria feliz em tentar desacoplar as melhorias da API e, por exemplo, focar apenas em explorar o espaço da solução em torno do aprimoramento da API relacionada aos adereços neste problema.

@kmalakoff O tópico do tópico é definido pelo título e pela primeira postagem. O título é componentDidReceiveProps Please not lots of general ways to reduce boilerplate . Este tópico é sobre como reduzir o clichê especificamente por meio da introdução de um método de ciclo de vida muito específico. Essa discussão por si só já é matizada o suficiente, sem introduzir tópicos adicionais (como renomear todas as funções) que já estão sendo discutidos em outras edições.

Seus pontos são valiosos e eu encorajo você a ter uma discussão, apenas mova-a para um tópico diferente, porque queremos manter os tópicos do github focados no tópico em questão. Caso contrário, as discussões são muito difíceis de rastrear / gerenciar. Tópicos existem para a maioria dos tópicos que você levantou.

@jimfb, por que as pessoas estão pedindo componentDidReceiveProps ? O motivo pelo qual estou interessado nisso é reduzir o clichê relacionado aos adereços . Não tenho certeza de como afirmar isso de forma mais clara para que eu possa ser ouvido e compreendido.

Você pediu os problemas e casos de uso, e eu declarei isso como o problema para mim e mostrei um caso de uso acima (respondendo a @ shea256) que parece dentro do tópico, dentro do escopo e, da minha perspectiva, importante para resolver esse problema.

Eu concordei em adiar a expansão do escopo para questões / melhorias gerais da API e vontade. Eu prometo! :piscadela:

Para sua informação: como afirmei anteriormente, o motivo pelo qual comecei o experimento de pensamento em torno deste problema foi porque os argumentos pareciam um pouco focados muito estreitos (por exemplo, já podemos fazer tudo o que precisamos com a API atual) em vez de identificar as motivações subjacentes por trás do edição. Como você sabe, às vezes você precisa recuar, focar, ver de um ângulo diferente, revisitar suposições, investigar os motivos subjacentes à solicitação, etc. para iterar em direção à melhor solução para um problema. Esperançosamente, eu ajudei a chamar a atenção para isso e minha participação ajudará a resolver esse problema para nossa satisfação! (é claro, também espero que a API React evolua para ser mais amigável e continue essa discussão em outro lugar ... obrigado pelos links)

@kmalakoff , ok, para reduzir o clichê, você apenas

@ me-andre isso é absolutamente uma opção se esse problema não levar a uma melhoria da API.

Esta questão foi levantada para promover a discussão sobre o desejo de fazer uma alteração na API para melhorá-la. A exploração de alternativas no código do cliente definitivamente deve ser considerada, mas se houver um caso forte de que a API deve ser alterada, esse tipo de solução alternativa seria desnecessário. A fim de contra-argumentar, os casos também precisam ser feitos sobre o que uma API melhorada deve ser e, em seguida, avaliados em relação à API atual para avaliá-los.

Por exemplo, se você demonstrasse que a API atual sem definir uma superclasse personalizada poderia ser usada de forma a reduzir o clichê (por exemplo, estamos usando a API incorretamente ou há recursos na API atual que podem ser usados ​​para alcançar um nível semelhante de redução de boilerplate, etc) ou provar por que não há melhoria possível (por exemplo, não existem soluções gerais porque há um caso de uso importante que não poderia ser suportado em nossas opções de melhoria de API), etc, você poderia fazer um caso de que a API é boa o suficiente como está.

Se a API requer que as pessoas escrevam superclasses customizadas para padrões de fluxo de controle básicos e comuns, isso reforça o argumento de que há um caso para a API ser aprimorada.

Curioso - qual é a razão de não ter componentWillReceiveProps acionado antes da montagem? Se você pensar sobre isso, o componente está realmente recebendo props quando é inicializado. Simplesmente não está recebendo novos adereços.

Quais são os casos de uso em que você só deseja acionar algo ao receber novos props (e não na recepção inicial do prop)?

Posso estar faltando algo óbvio aqui, mas apenas tentando conciliar os pontos de vista das várias partes.

Em qualquer caso, se componentWillReceiveNewProps fosse importante em alguns casos, ainda se poderia simulá-lo com um componentWillReceiveProps modificado, verificando os adereços que chegam.

@kmalakoff se componentWillReceiveProps fosse acionado pela primeira vez, isso atenderia aos seus padrões de simplificação de API?

componentWillReceiveProps não ser ativado na montagem não é o motivo pelo qual as pessoas estão pedindo componentDidReceiveProps . As pessoas estão pedindo componentDidReceiveProps porque escreveram todos os seus métodos para acessar this.props . Quando componentWillReceiveProps é chamado, os nextProps são passados, mas this.props ainda não mudou, o que significa que temos que passar nextProps para todos os métodos que são chamados em resposta a componentWillReceiveProps , em vez de deixá-los como estão. Terminamos com uma tonelada de props = props || this.props e uma tonelada de props em cada função que chamamos.

@ pontos positivos

É por isso que estou tentando expandir o espaço de solução que está sendo considerado (por exemplo, também chamar o init), uma vez que poderia realmente haver uma solução ainda melhor para reduzir mais o boilerplate da hélice.

Espero que possamos ir mais longe:

Antes do Novo Gancho

componentWillMount() {
  this.setup(this.props.id);
}

componentWillReceiveProps(next) {
  this.setup(next.id);
}

setup(id) {
  UserActions.load(id);
}

Depois de novo gancho (revisado)

componentDidReceiveProps(prevProps) {
  UserActions.load(this.props.id);
}

ou se UserActions.load não puder verificar o ID atualmente carregado:

componentDidReceiveProps(prevProps) {
  if (!prevProps || (prevProps.id !== this.props.id))
    UserActions.load(this.props.id);
}

@kmalakoff , o que estou falando é que a melhoria da API está absolutamente disponível para você agora: você pode fazer sua própria fábrica de componentes e depois compartilhar conosco (junto com exemplos de casos de uso). Isso tornará sua proposta e seus fundamentos cem vezes mais claros. Uma vez que todos os pontos do ciclo de vida já têm retornos de chamada apropriados, você pode introduzir qualquer novo método em qualquer ponto do ciclo de vida / estado do componente. Você pode até mesmo misturar um emissor de evento em seu componente e tornar possível anexar vários manipuladores para uma mudança de estado.

@ shea256 , uma possível razão para componentWillReceiveProps não ser acionado antes da primeira renderização é que não existe this.props naquele momento. O que você normalmente faz em componentWillReceiveProps é comparar this.props[propName] com newProps[propName] . Ter o método acionado antes da primeira renderização faria com que você também tivesse que verificar a presença de this.props . Além disso, o componente inteiro está completamente não inicializado no momento em que recebe props antes da renderização inicial, ele nem mesmo tem state .

@kmalakoff , eu postei duas vezes exemplos de código que mostram como organizar um componente React de uma forma que ele não precise de setup ou hacks semelhantes. Você poderia dizer por que você ainda se esforça para mudar o comportamento de um componente React em vez de ajustar seu código para que ele se integre ao React? Seria ótimo se você apenas apontasse onde meus exemplos são inadequados para o seu caso de uso.

@ akinnee-gl, a razão para não introduzir um novo método apenas para acessar this.props na atualização é que temos esse método - é chamado render() . Ele até é chamado após uma verificação de shouldComponentUpdate() - que normalmente é um lugar onde você faz this.props !== newProps ou _.isEqual(this.props, newProps) etc.
Se você acha que deveria ter um método separado para alguma configuração, por que não criar uma subclasse de um componente React e definir um método render() a seguir

this.setup(this.props);
return this._render();

Só não vejo como isso simplifica as coisas no ecossistema React, mas é isso que você sempre pede.

@ me-andre, a premissa desse problema não é que não possamos alcançar o que queremos com a API atual, nem que não possamos contornar a API atual no código do cliente. A premissa desse problema é que a API React atual está abaixo do ideal e precisa ser melhorada ; por exemplo, se você propusesse princípios de como seria uma API ideal (como tentei acima), a API React atual teria pontuação em um intervalo de nível médio porque é subótima / deficiente em várias áreas.

Infelizmente, fornecer maneiras no código do cliente para contornar a API React não aborda as causas raiz do problema, desvia o debate de abordar os problemas subjacentes e não levará a um debate em torno de soluções potenciais para melhorar a API React.

Resumindo, já tenho soluções alternativas que estão funcionando para mim porque tenho um monte de aplicativos React em produção, então as práticas recomendadas podem ser ótimas para uma postagem de blog, mas usá-las como uma linha de debate nesta questão apenas nos desviará de um verdadeiro debate sobre o verdadeiro propósito deste problema: como melhorar a API React (neste caso, limitado a casos de uso prop e clichê).

O React 1.0 deve apontar para o topo, não para o meio. Um grande aumento na versão pode quebrar a compatibilidade com versões anteriores, então vamos buscar a melhor API possível com base no que aprendemos em todos esses anos de uso da versão 0.x.

(Para sua informação: acho que as pessoas podem não se envolver com seus exemplos tanto quanto você espera, porque não estão vindo aqui para aprender sobre a API atual, mas porque estão procurando / esperando por melhorias na API, por exemplo, eles pode ser percebido como com boas intenções, mas ligeiramente desalinhado)

Acho que as pessoas podem não se envolver com seus exemplos tanto quanto você espera, porque não estão vindo aqui para aprender sobre a API atual, mas porque estão procurando / esperando melhorias para a API

Ok, você vem com uma proposta de melhoria de API, mas depois mostra o código que está muito longe de "Práticas recomendadas de React". Algumas coisas parecem muito com as piores práticas. Aí você fala: "esse é o motivo da mudança". Sim, esse é o motivo da mudança, mas não na base de código do React.
Eu mostro como reorganizar o código para fazê-lo funcionar bem com o React, mas apenas ignore a demonstração de uso adequada e continue insistindo. Isso não parece ser um debate construtivo.

Infelizmente, fornecer maneiras no código do cliente para contornar a API React não aborda as causas raiz do problema

Quando você envolve uma API de baixo nível com uma API de nível superior, isso não é uma solução alternativa, mas uma prática comum. Muitos frameworks brilhantes seguem essa ideia.
O que eu continuo dizendo é envolver a feia API existente com o que você gostaria de usar e compartilhar conosco. Junto com os exemplos de uso e explicações do porque melhorou seria incrível.
Novamente, eu simplesmente não vejo uma razão para você simplesmente não fazer isso. Porque se o que você está falando for um problema comum, isso seria de grande ajuda para muitas pessoas.

Eu tenho um monte de aplicativos React em produção, então as práticas recomendadas podem ser ótimas para uma postagem de blog, mas usá-los como uma linha de debate nesta edição apenas nos desviará de um debate real

O que você normalmente faz ao arquitetar / projetar uma API é prever problemas, fazer hipóteses sobre casos de uso, etc. A razão pela qual as pessoas fazem hipóteses não é que tentem se abstrair de um mundo real em busca do ideal. É apenas falta de experiência - você não pode obter experiência "com antecedência".

Você diz que tem essa experiência, viu problemas reais e tem algumas soluções alternativas que pode compartilhar. É exatamente isso que pode tornar esse debate "real" e "efetivo". O próprio React foi construído com base em problemas e desafios reais - é por isso que ele resolve problemas reais de arquitetura e não apenas "como escrever hello world em 3 linhas".

@ me-andre, ouvi você e sua linha de argumentação é clara.

Você está correto ao dizer que o ponto central do meu argumento é que se estabelecermos princípios melhores com base em nossas experiências coletivas de uso da API React atual e abrirmos o debate para incluir soluções não dogmáticas que podem mudar a API de algumas maneiras fundamentais, podemos e devemos aproveite a oportunidade de melhorar a API React para torná-la ainda melhor do que é hoje. Não vamos descansar sobre os louros quando há espaço para melhorar!

Como você avaliaria a API React atual em torno de adereços? (digamos usando notas de letras: F a A +), por que e o que você faria para melhorá-lo se fosse menor que A +?

@ me-andre, você teve a chance de preparar sua classificação e análise? Estou interessado em ouvir o que você acredita serem os pontos fortes, fracos e oportunidades da API atual.

+1

+1 por favor

Existe uma solução alternativa? Eu preciso de ComponentDidReceiveProps

Eu fiz esse problema há mais de um ano e tenho usado o React diariamente desde então. Não acho mais que haja um caso de uso para componentDidReceiveProps que justifique o aumento da API.

@AlexCppns o que você está tentando fazer?

@iammerrick , na verdade está tudo bem, eu apenas não entendi como o componentWillReceiveProps é usado.

Já me deparei com isso algumas vezes, e o que acabei fazendo foi:

componentWillReceiveProps(nextProps) {
  if (this.props.foo !== nextProps.foo) this.needsUpdate = true;
}
componentDidUpdate() {
  if (this.needsUpdate) {
    this.needsUpdate = false;
    // update the dom
  }
}

Não é tão ruim.

@brigand , não há necessidade de uma bandeira - você pode comparar adereços dentro de componentDidUpdate() :

componentDidUpdate(prevProps) {
    let needsUpdate = prevProps.foo !== this.props.foo;
    // ...whatever
}

Além disso, sua solução pode ser facilmente quebrada por shouldComponentUpdate() , o que pode impedir a re-renderização.

Oh legal, obrigado!

@jimfb Acho que tenho um caso de uso síncrono abaixo. Acho que componetDidReceiveProps teria sido perfeito para isso.

  componentDidMount() {
    this._selectAll()
  }

  componentDidUpdate(prevProps) {
    let shouldUpdateSelected = (prevProps.recordTypeFilter !== this.props.recordTypeFilter) ||
      (prevProps.statusFilter !== this.props.statusFilter) ||
      (prevProps.list !== this.props.list)

    if (shouldUpdateSelected) { this._selectAll() }
  }

  _selectAll() {
    this.setState({ selectedIds: this._getFilteredOrders().map((order) => ( order.id )) })
  }

  _getFilteredOrders() {
    let filteredOrders = this.props.list

    // recordTypeFilter
    let recordTypeFilter = this.props.recordTypeFilter
    filteredOrders = _.filter(filteredOrders, (order) => {
        // somelogic
    })

    // statusFilter
    let statusFilter = this.props.statusFilter
    filteredOrders = _.filter(filteredOrders, (order) => {
        // somelogic
    })

    return filteredOrders
  }

@chanakasan , seu exemplo carece do método render() , que é essencial para entender seu exemplo e sugerir uma solução melhor.
Em segundo lugar, seu código está conectado a alguma lógica de negócios personalizada e não é fácil de ler. Não hesite em explicar, não apenas jogue um copy-paste nos outros.
No entanto, li seu exemplo e gostaria de compartilhar as seguintes idéias:

  1. Este é o caso de uso de componentWillReceiveProps , não componentDidUpdate . Se você alternar para componentWillReceiveProps , seu componente será renderizado novamente duas vezes menos, preservando a mesma lógica. Depois de um ano desde que deixei esta discussão, ainda não vejo nenhum caso de uso para componentDidUpdate exceto para reagir às alterações do DOM.
  2. Muito melhor, entretanto, seria evitar ganchos e mover toda a lógica para o método render() / migrar para um componente sem estado, porque this.state.selectedIds não é realmente um estado. É puramente calculado a partir de adereços.

Olá @ me-andre, Obrigado por responder. Eu li a discussão nesta página e gostaria de agradecer a você por participar dela.

Sim, componentDidReceiveProps não é necessário, mas as coisas parecem misteriosas sem ele.
Você disse your component would re-render twice as less se eu usar componentWillReceiveProps . Isso ainda é um mistério para mim.

Eu pensei e tentei as duas coisas que você sugeriu antes, mas falhei.

  1. componentWillReceiveProps não funcionará porque a função _getFilteredOrders() chamada de _selectAll() precisa de newProps.
  2. Não consegui pensar em uma maneira de derivar selectedIds sem armazená-lo no estado.

Acabei de criar um exemplo completo do meu caso de uso. Tornei mais fácil de entender o máximo possível.

Se você pudesse dar uma olhada e me apontar na direção certa. Se eu conseguir entender isso corretamente, compartilharei esse exemplo em uma postagem de blog para ajudar outras pessoas também.

@chanakasan , vamos, isso parece um código de produção. Não vou ler tudo e ajudá-lo em seu projeto de graça.

No entanto, posso apontar a direção certa:

  1. Tanto componentWillReceiveProps() quanto componentDidUpdate() podem acessar os adereços anteriores e posteriores. Isso é visto claramente nos documentos oficiais do React.
  2. A partir do copypaste completo de seu código, agora é óbvio que você usa o estado para armazenar ids selecionados que um usuário pode alternar. É bom usar o estado então, mas ainda componentWillReceiveProps() acionaria a re-renderização duas vezes menos. (porque uma renderização vai acontecer depois de componentWillReceiveProps() qualquer maneira, setState() apenas atualizaria o estado da próxima renderização).
  3. Por favor, dê uma olhada na documentação . Economize seu tempo e respeite os outros.

@ me-andre, acho que entendi seu ponto sobre componentWillReceiveProps usando esta linha nos documentos:

componentWillReceiveProps () não é invocado se você apenas chamar this.setState ()

Mas a advertência de usar componentWillReceiveProps é que terei que passar nextProps para as funções.

Vou tentar seguir seu conselho. Obrigado, Eu agradeço.

A propósito, não era minha intenção que você ajudasse no meu projeto de produção de graça :). É um exemplo mais completo do meu curto exemplo anterior para preencher quaisquer lacunas sobre meu caso de uso.

e se usarmos isso em conjunto com o shouldComponentUpdate?
onde não desejamos atualizar o estado do componente ou renderizar novamente,
mas precisamos que this.props use os mais recentes props para fazer algum trabalho de script manual após o recebimento dos props

minha solução é definir this.props para os novos adereços antes de executar a função desejada

Seria realmente bom ter o ComponentDidMount ou algum gancho para isso. Porque? Porque, às vezes, precisamos fazer outros cálculos que não dependem do ciclo de vida do React.

Por exemplo: eu tenho um componente pai que pode ser redimensionado. O componente filho é responsável por renderizar um mapa OpenLayer que tem uma função responsável por redimensionar o mapa. No entanto, isso deve acontecer depois que a criança receber os acessórios dos pais e também realizar outros cálculos no ciclo de vida do React.

Esta página foi útil?
0 / 5 - 0 avaliações