Convertendo React.createClass para React.Component
No meu último artigo falei sobre como corrigir versões mais antigas do React para funcionar em um ambiente moderno. Neste artigo vou mais longe e dou um guia passo a passo para atualizar um componente React clássico para um moderno, mudando a React.createClassmaneira de fazer as coisas para o método moderno e preferido React.Component.
Os arquivos que precisamos estarão nas pastas ‘Original’ e ‘Final’ do diretório de componentes na solução GitHub que acompanha.
Obter arquivos de tutorial do GitHub
Novamente trabalhando com o componente [react-checkbox-list](https://github.com/sonyan/react-checkbox-list) da Sony An (disponível como react-checkbox-listem npm ). Este guia mostra passo a passo como substituir a estrutura e os métodos de um React.createClasscomponente clássico por um moderno React.Component.
Começando com o .jsxarquivo na react-checkbox-listsolução, primeiro vamos excluir o .jsarquivo e renomeá-lo, .jspois os .jsxarquivos não precisam mais ser nomeados de forma diferente.
Isso nos dá o seguinte código inicial:
'use strict';
var React = require('react');
module.exports = React.createClass({
displayName: 'CheckBoxList',
propTypes: {
defaultData: React.PropTypes.array,
onChange: React.PropTypes.func
},
getInitialState: function() {
return {
data: this.props.defaultData || []
};
},
handleItemChange: function(e) {
var selectedValues = [],
newData = [];
this.state.data.forEach(function(item) {
if(item.value === e.target.value) {
item.checked = e.target.checked;
}
if(item.checked) {
selectedValues.push(item.value);
}
newData.push(item);
});
this.setState({data: newData});
if(this.props.onChange) {
this.props.onChange(selectedValues);
}
},
reset: function() {
var newData = [];
this.state.data.forEach(function(item) {
item.checked = false;
newData.push(item);
});
this.setState({data: newData});
},
checkAll: function() {
var newData = [];
this.state.data.forEach(function(item) {
item.checked = true;
newData.push(item);
});
this.setState({data: newData});
},
render: function() {
var options;
options = this.state.data.map(function(item, index) {
return (<div key={'chk-' + index} className="checkbox">
<label>
<input
type="checkbox"
value={item.value}
onChange={this.handleItemChange}
checked={item.checked? true: false} /> {item.label}
</label>
</div>
);
}.bind(this));
return (<div>
{options}
</div>
);
}
});
Convertendo o componente
Ao tentar carregar este código, o primeiro erro que recebemos éUncaught Error: Module build failed: SyntaxError: The @jsx React.DOM pragma has been deprecated as of React 0.12
Erro não capturado: Falha na compilação do módulo: SyntaxError: O pragma @jsx React.DOM foi preterido a partir do React 0.12
É simples o suficiente para corrigir isso. Basta remover a linha /** @jsx React.DOM */da parte superior do documento.
Agora temos Uncaught TypeError: Cannot read property 'array' of undefinedo mesmo erro do tutorial de patch. Isso ocorre porque o React.propTypes foi preterido na versão 15.50 do React, então de acordo com esse tutorial, vá em frente e instale o pacote PropTypes com o comando via npm. Vamos importar isso para o nosso pacote mais tarde.[React.createClass](https://wholesomecode.ltd/blog/broken-react-createclass-component-lets-fix-it/)
npm install --save prop-types
O que se segue agora é uma revisão completa da base de código, então não podemos atualizar e corrigir um erro como fizemos anteriormente, então não saberemos se funcionou até o final. Preparar-se!
Vamos começar substituindo a React.createClassfunção declarando um novo React.Component. Altere nosso código para que fique assim:
'use strict';
import React from 'react';
class CheckBoxList extends React.Component {
...
}
Observe que fizemos algumas coisas aqui:
- Removido o comentário JSX de abertura
- Alterado o tipo de declaração
var React =...paraimport React from 'react';este é a maneira moderna de fazer declarações no React. - Agora declaramos uma nova classe de
CheckBoxList, em vez de exportar uma função. - Devido à sintaxe do objeto agora estar entre parênteses
{...}, precisamos descartar o fechamento);
No entanto, esta nova classe agora está faltando uma maneira de exportá-la para que outros componentes possam usá-la, então vamos adicionar uma declaração de exportação na parte inferior desse código.
'use strict';
import React from 'react';
class CheckBoxList extends React.Component {
...
}
export default CheckBoxList;
Em nosso código original, a primeira linha de código na função era displayName: 'CheckBoxList',nossa exportação agora lida com isso, para que possamos remover esse código completamente.
A próxima linha declara propTypesque estes agora estão fora da classe e precisam da PropTypesdependência que adicionamos via npm. Vamos adicionar isso em nossas importações e escrever os PropTypes assim:
'use strict';
import React from 'react';
import PropTypes from 'prop-types';
class CheckBoxList extends React.Component {
...
}
CheckBoxList.propTypes = {
defaultData: PropTypes.array,
onChange: PropTypes.func,
};
export default CheckBoxList;
Em seguida, o estado é declarado através do método getInitialStatecom React.Componentque definimos nosso estado inicial com um construtor. Adicione o seguinte código em nossa compilação:
...
class CheckBoxList extends React.Component {
constructor( props) {
super( props );
this.state = {
data: [],
}
}
...
}
...
Lá vamos nós, que configura o estado do componente, mas espere, nós ainda não dissemos a ele de onde obter seu estado. É aí que componentWillMountvem a calhar.
...
class CheckBoxList extends React.Component {
constructor( props) {
super( props );
this.state = {
data: [],
}
}
componentWillMount() {
this.setState({
data: this.props.defaultData,
});
}
...
}
...
Uma coisa importante a ser observada é que os métodos internos de React.Componentnão terminam em vírgulas (,), portanto, certifique-se de que quaisquer métodos adicionados ao componente não terminem em vírgula!
Em seguida, vamos adicionar a função de renderização de volta. Esta é provavelmente a parte mais fácil, é quase a mesma coisa, com a diferença chave que mudamos render: function() {para apenas render() {.
...
class CheckBoxList extends React.Component {
...
render() {
var options;
options = this.state.data.map(function(item, index) {
return (<div key={'chk-' + index} className="checkbox">
<label>
<input
type="checkbox"
value={item.value}
onChange={this.handleItemChange}
checked={item.checked? true: false} /> {item.label}
</label>
</div>
);
}.bind(this));
return (<div>
{options}
</div>
);
}
...
}
...
Para fins de manter este tutorial direto ao ponto, não converterei os dois métodos resete, checkAllno entanto, os conceitos usados para a porta do método handleItemChangeapple para ambos, portanto, sinta-se à vontade para experimentá-los.
Para migrar o handleItemChangemétodo, primeiro precisamos remover a vírgula de fechamento (,) e alterar o tipo de declaração da função handleItemChange: function(e) {para handleItemChange( e) {garantir que ainda passemos ecomo parâmetro do evento.
Vamos adicionar isso acima do nosso método de renderização.
...
class CheckBoxList extends React.Component {
...
handleItemChange( e) {
var selectedValues = [],
newData = [];
this.state.data.forEach(function(item) {
if(item.value == e.target.value) {
item.checked = e.target.checked;
}
if(item.checked) {
selectedValues.push(item.value);
}
newData.push(item);
});
this.setState( {data: newData} );
if(this.props.onChange) {
this.props.onChange(selectedValues);
}
}
...
}
...
Nosso componente agora será renderizado, porém não será funcional. Ao tentar clicar em uma caixa de seleção, você receberá o erro Uncaught TypeError: Cannot read property 'state' of undefined.
Erro de tipo não capturado: não é possível ler a propriedade ‘estado’ de indefinido
Isso porque em nosso handleItemChangemétodo, onde tentamos acessar o estado, thisé indefinido. Para corrigir isso, precisamos vincular nossa função thisadicionando a seguinte linha em nosso construtor: this.handleItemChange = this.handleItemChange.bind( this );.
...
class CheckBoxList extends React.Component {
constructor( props) {
super( props );
this.state = {
data: [],
}
this.handleItemChange = this.handleItemChange.bind( this );
}
...
}
...
E aí está, nosso recém-convertido componente React em ação.
React.createClass para React.Component
Código-fonte do tutorial
Você pode baixar o código-fonte da versão original e final do componente no GitHub. O plugin contém um bloco WordPress Gutenberg que você pode usar para brincar com o código, com três arquivos que você pode excluir conforme aplicável:
Os arquivos de tutorial do GitHub para a conversão de React.createClass para React.Component