Estado de inicialização do componente React a partir de adereços

204

No React, existem diferenças reais entre essas duas implementações? Alguns amigos me dizem que o FirstComponent é o padrão, mas não vejo o porquê. O SecondComponent parece mais simples porque a renderização é chamada apenas uma vez.

Primeiro:

import React, { PropTypes } from 'react'

class FirstComponent extends React.Component {

  state = {
    description: ''
  }

  componentDidMount() {
    const { description} = this.props;
    this.setState({ description });
  }

  render () {
    const {state: { description }} = this;    
    return (
      <input type="text" value={description} /> 
    );
  }
}

export default FirstComponent;

Segundo:

import React, { PropTypes } from 'react'

class SecondComponent extends React.Component {

  state = {
    description: ''
  }

  constructor (props) => {
    const { description } = props;
    this.state = {description};
  }

  render () {
    const {state: { description }} = this;    
    return (
      <input type="text" value={description} />   
    );
  }
}

export default SecondComponent;

Atualização: Alterei setState () para this.state = {} (obrigado joews). No entanto, ainda não vejo a diferença. Um é melhor que outro?

Levy Moreira
fonte
10
Por que você está armazenando seus adereços no estado? Você deve usar seus props diretamente, em vez de colocar um valor em cache. Leia Por que definir adereços como estado em React.js é blasfêmia e adereços em getInitialState é um antipadrão .
precisa
12
Um exemplo - um componente alternável (por exemplo, uma janela ou gaveta). O pai sabe se o componente deve começar aberto ou fechado; o próprio componente pode saber se está aberto ou não em um ponto no tempo. Nesse caso, acho que this.state = { isVisible: props.isVisible }faz sentido. Depende de como o aplicativo distribui o estado da interface do usuário.
Johs15
2
Você deve ler este medium.com/@justintulk/…
FDisk
5
Em 2017, o Facebook demonstra o uso de adereços para definir o estado inicial em sua documentação: reactjs.org/docs/react-component.html#constructor
Rohmer
1
@ Aurora0001 Em uma situação em que você precisa manipular um formulário, diga um formulário de edição que faça solicitações de rede por conta própria, mas você precisará inicializar as entradas com valores que virão como adereços para esse componente. Para manter a forma dinâmica, esses valores devem ser mantidos em estado.
Eric McWinNEr 12/08/19

Respostas:

196

Note-se que é um anti-padrão copiar propriedades que nunca mudam para o estado (basta acessar .props diretamente nesse caso). Se você tiver uma variável de estado que mudará eventualmente, mas começa com um valor de .props, você nem precisa de uma chamada de construtor - essas variáveis ​​locais são inicializadas após uma chamada ao construtor do pai:

class FirstComponent extends React.Component {
  state = {
    x: this.props.initialX,
    // You can even call functions and class methods:
    y: this.someMethod(this.props.initialY),
  };
}

Esta é uma abreviação equivalente à resposta de @joews abaixo. Parece funcionar apenas em versões mais recentes dos transpilers es6, tive problemas com ele em algumas configurações do webpack. Se isso não funcionar, tente adicionar o plug-in babel babel-plugin-transform-class-propertiesou use a versão não abreviada de @joews abaixo.

Zane Hooper
fonte
1
você pode explicar mais como sua resposta é diferente da resposta @joews?
Jalal
3
Adicionado "Você pode pular a chamada do construtor se tudo o que você está fazendo é definir variáveis."
Zane Hooper
3
Se não funcionar, você provavelmente precisará instalar este plug-in babel "babel-plugin-transform-class-properties".
Faheem
2
Não é um anti-padrão inicializar o estado a partir de adereços se entendermos que o estado não depende dos adereços após a inicialização. Se você está tentando manter os dois sincronizados, é um anti-padrão.
Yatrix
1
@ ak85 é a mesma sintaxe, mas você usaria this.state. Esta sintaxe é apenas uma sintaxe atalho para definir o estado durante o processo de construção de classe (e pode ser usado para outros fins que estado variáveis como bem)
Zane Hooper
137

Você não precisa chamar setStateum componente constructor- é idiomático definir this.statediretamente:

class FirstComponent extends React.Component {

  constructor(props) {
    super(props);

    this.state = {
      x: props.initialX
    };
  }
  // ...
}

Consulte React docs - Adicionando estado local a uma classe .

Não há vantagem para o primeiro método que você descreve. Isso resultará em uma segunda atualização imediatamente antes da montagem do componente pela primeira vez.

joews
fonte
4
Boa resposta. Pode ser interessante notar que isso é apenas para definir o estado inicial; você ainda precisará usá- setStatelo se o modificar em qualquer outro momento; caso contrário, as alterações poderão não ser renderizadas.
Aurora0001
Obrigado novamente jowes, em segundo lugar a documentação facebook.github.io/react/docs/…
Levy Moreira
(desculpe, pressione enter ..) devemos usar o getInitialState para definir os props para declarar, em tarefas mais complexas, se for simples, podemos apenas usar o this.props na renderização, correto?
Levy Moreira
1
Em uma nota marginal: use super(props)no construtor. Discussão sobre SO
cutemachine
2
A sugestão de joews funciona na maioria dos casos, mas tenha cuidado ao enviar adereços para this.state diretamente. Copiar adereços para this.state é realmente uma única fonte de verdade ( medium.com/react-ecosystem/… ). Além disso, Dan Abramov sugeriu uma vez não armazenar os valores dos adereços no estado. ( twitter.com/dan_abramov/status/749710501916139520/photo/1 ).
precisa
33

Atualização para o React 16.3 alpha introduzida static getDerivedStateFromProps(nextProps, prevState)( docs ) como um substituto para componentWillReceiveProps.

getDerivedStateFromProps é chamado depois que um componente é instanciado e quando recebe novos adereços. Ele deve retornar um objeto para atualizar o estado ou nulo para indicar que os novos adereços não exigem nenhuma atualização de estado.

Observe que, se um componente pai fizer com que seu componente seja renderizado novamente, esse método será chamado mesmo que os acessórios não tenham sido alterados. Você pode comparar valores novos e anteriores se quiser lidar apenas com as alterações.

https://reactjs.org/docs/react-component.html#static-getderivedstatefromprops

É estático, portanto, não tem acesso direto a this(no entanto, tem acesso a prevState, o que poderia armazenar coisas normalmente associadas a, thispor exemplo refs)

editado para refletir a correção de @ nerfologist nos comentários

Ashley Coolman
fonte
3
Só para esclarecer, é nomeado getDerivedStateFromProps(marcar a letra maiúscula em Props) e os parâmetros são nextProps, prevState(não nextState): reactjs.org/docs/...
nerfologist
1
Uau! podemos usar isso para atualizar o estado quando os adereços atualizados são recebidos!
Aromal Sasidharan
2
Ainda precisamos criar o estado inicial no construtor, considerando que ele getDerivedStateFromPropssempre é chamado antes da renderização inicial?
bvdb
19

Você pode usar o formulário curto como abaixo se quiser adicionar todos os adereços ao estado e manter os mesmos nomes.

constructor(props) {
    super(props);
    this.state = {
       ...props
    }
    //...
}
dacharjaya
fonte
1
é um anti-padrão copiar propriedades que nunca mudam para o estado. É melhor descrever explicitamente quais campos seu componente usa.
Michael Freidgeim 18/05/19
5

defina os dados do estado dentro do construtor como este

constructor(props) {
    super(props);
    this.state = {
      productdatail: this.props.productdetailProps
    };
  }

ele não funcionará se você definir o método side componentDidMount () por meio de adereços.

Krishna Kumar Jangid
fonte
3

Se você iniciar diretamente o estado a partir de adereços, ele mostrará um aviso no React 16.5 (5 de setembro de 2018)

Sujith S
fonte
alguma idéia de por que isso vai avisar?
Nitin Jadhav
2
Parece que é apenas se você usar state = props. Mais informações aqui: github.com/facebook/react/pull/11658#issuecomment-419677176
agora
1

Você pode usar componentWillReceiveProps.

constructor(props) {
    super(props);
    this.state = {
      productdatail: ''
    };
  }

    componentWillReceiveProps(nextProps){
        this.setState({ productdatail: nextProps.productdetailProps })
    }
Ankit Kumar Rajpoot
fonte
1
componentWillReceiveProps está obsoleto não pode ser usado para versões futuras
Vivek Ghanchi 13/01
1

É necessário ter cuidado ao inicializar a statepartir propsdo construtor. Mesmo se propsalterado para um novo, o estado não seria alterado porque a montagem nunca acontecerá novamente. Então getDerivedStateFromPropsexiste para isso.

class FirstComponent extends React.Component {
    state = {
        description: ""
    };

    static getDerivedStateFromProps(nextProps, prevState) {
        if (prevState.description !== nextProps.description) {
          return { description: nextProps.description };
        }

        return null;
    }

    render() {
        const {state: {description}} = this;    

        return (
            <input type="text" value={description} /> 
        );
    }
}
Yonggoo Noh
fonte