Estruturando Componentes
@lucianomlima
@rodrigopr
Meetup React Salvador #5
Recapitulando...
Um componente pode ser definido de várias formas
Pode ser uma função
Functional Components
import React from 'react';
function Welcome(props) {
return <h1>Hello {props.name}</h1>;
}
function Text(props) {
return <div>{props.text}</div>;
}
function App() {
return (
<React.Fragment>
<Welcome name="Johnny" />
<Text text="Your App is Ready!" />
</React.Fragment>
);
}
Functional Components
import React from 'react';
const Welcome = props => (<h1>Hello {props.name}</h1>);
// const Welcome =({ name }) => (<h1>Hello {name}</h1>);
const Text = props => (<div>{props.text}</div>);
// const Text =({ text }) => (<div>{text}</div>);
const App = () => (
<React.Fragment>
<Welcome name="Johnny" />
<Text text="Your App is ready!" />
</React.Fragment>
);
( with arrow functions )
Pode ser uma classe
Class Components
import React from 'react';
const Welcome =({ name }) => (<h1>Hello {name}</h1>);
const Text =({ text }) => (<div>{text}</div>);
class App extends React.Component {
componentDidMount() {
doSomething();
}
render() {
<React.Fragment>
<Welcome name="Johnny" />
<Text text="Your App is ready!" />
</React.Fragment>
}
}
Stateless / Stateful
Stateless
import React from 'react';
const Avatar = ({ name, image }) => (<img src={image} alt="name" />);
const Welcome = props => (
<div className="Welcome">
<Avatar {...props} />
<h1>Hello {props.name}</h1>
</div>
);
// OR
const UserInfo = props => (
<div className="UserInfo">
<Avatar {...props} />
<div className="UserInfo-name">
<span>{props.name}</span>
</div>
</div>
);
Stateful
import React from 'react';
import MONTH_NAMES from './somewhere';
class Clock extends React.Component {
state = {
date: new Date()
}
getDate(date) {
const month = MONTH_NAMES[date.getMonth()];
return `${date.getDate()}/${month}/${date.getFullYear()}`;
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.getDate(this.state.date)}.</h2>
</div>
);
}
}
Sua aplicação cresce...
É preciso pensar em:
Evitar duplicação de código
Compartilhar comportamentos
Pensar de forma escalável
Pensar no impacto na performance
Pensar na rastreabilidade de bugs
Controlled / Uncontrolled
Controlled vs Uncontrolled
Quem deve
controlar o state?
Uncontrolled Components
import React from 'react';
class Form extends React.Component {
...
handleSubmitClick = (e) => {
e.preventDefault();
const name = this._name.value;
myCustomSubmit(name);
}
render() {
return (
<form>
<input type="text" ref={input => this._name = input} />
<button onClick={this.handleSubmitClick}>Sign up</button>
</form>
);
}
}
Controlled Components
import React from 'react';
class Form extends React.Component {
state = {
name: ''
}
handleNameChange = (event) => {
this.setState({ name: event.target.value });
}
handleSubmitClick = (e) => {
e.preventDefault();
myCustomSubmit(this.state.name);
}
render() {
return (
<form>
<input type="text" value={this.state.name} onChange={this.handleNameChange} />
<button onClick={this.handleSubmitClick}>Sign up</button>
</form>
);
}
}
Problem: Null Value
Controlled Components
Mais genéricos, maior reutilização
Componentes pais tem mais liberdade em como lidar com um componente filho
Uncontrolled Components
Mais simples, menos código envolvido
Ganha complexidade quando necessita suportar vários casos de uso
Não há bala de prata
class Toggle extends React.Component {
state = {
on: false
}
isControlled(prop) {
return this.props[prop] !== undefined;
}
getState() {
// Need to decide from where to get the value
return {
on: this.isControlled('on') ? this.props.on : this.state.on
};
}
toggle = () => {
if (this.isControlled("on")) {
this.props.onToggle(!this.getState().on);
} else {
// After update state, call onToogle to update parent
this.setState(
({ on }) => ({ on: !on }),
() => {
this.props.onToggle(this.getState().on);
}
);
}
}
render() {
return <Switch on={this.getState().on} onClick={this.toggle} />;
}
}
Compound Components
Controlled /
Uncontrolled
JSX
Compound Components
Vários componentes trabalhando juntos
Conversando entre si
Interligados intimamente
Children prop
<MyComponent>Hello world!</MyComponent>
<div>Hello World</div>
<div>
Hello World
</div>
<div>
Hello
World
</div>
<div>
Hello World
</div>
( String Literals )
Children prop
<MyContainer>
<MyFirstComponent />
<MySecondComponent />
</MyContainer>
<MyList>
Here is a list:
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
</MyList>
render() {
return [
<li key="A">First item</li>,
<li key="B">Second item</li>,
<li key="C">Third item</li>,
];
}
( JSX Children )
Children prop
<MyComponent>{'foo'}</MyComponent>
const Item = (props) => (<li>{props.message}</li>);
const List = () => {
const items = ['finish doc', 'submit pr', 'code review'];
return (
<ul>
{items.map(message => <Item key={message} message={message} />)}
</ul>
);
}
render() {
return [
<li key="A">`${first} item`</li>,
<li key="B">`${second} item`</li>,
<li key="C">`${third} item`</li>,
];
}
( JavaScript Expressions )
Children prop
import React from 'react';
// Calls the children callback numTimes to produce a repeated component
const Repeat = (props) => {
let items = [];
for (let i = 0; i < props.numTimes; i++) {
items.push(props.children(i));
}
return <div>{items}</div>;
}
const ListOfTenThings = () => (
<Repeat numTimes={10}>
{(index) => (
<div key={index}>
This is item number {index} in the list
</div>
)}
</Repeat>
);
export default ListOfTenThings;
( Functions as Children )
React.Children
Permite lidar com this.props.children
de diferentes formas
React.Children.map(children, function)
React.Children.forEach(children, function)
React.Children.count(children, function)
React.Children.only(children)
React.Children.toArray(children)
React.cloneElement
Retorna um novo React element
Novo elemento terá as props mescladas com as props do elemento original
Props key e ref serão preservadas
Make it flexible
Controlled /
Uncontrolled
Compound Components
High Order Components
High Order Component
"Dependency Injection" para componentes
Originado das High Order Functions
Isolamento de lógica para reaproveitamento
Isolamento de lógica para abstração
Isolamento de lógica para código mais limpo
Exemplo
HOC - Atenção
HOCs sempre retornam um novo componente!
Warnings
Não usar no render
Propriedades estática não são copiadas
Gera maior acoplamento de código
Pode tornar o debug mais difícil
Ref não é propagado automaticamente
HOC - Performance
import { connect } from '...';
import { compose, withState, ... } from '...';
const enhancer = compose(
connect(...),
withState(...),
withSomething,
);
const EnhancedComponent = enhancer(Component);
/*
|-- connect render
|-- withState render
|-- withSomething render
|-- Component render
*/
HOC - Testes
export const Component = (props) => {
return (<div> ... </div>);
};
const enhancer = compose(
...
);
export default enhancer(Component);
import EnhancedComponent, { Component } from '../';
describe('Component', () => {
// unit-test Component directly
});
describe('EnhancedComponent', () => {
// you can also do integration test
});
HOC - Você já usa!
( talvez só não saiba... )
Relay.createContainer()
Redux: connect()
MobX: observer()
react-loadable
Controlled /
Uncontrolled
Compound Components
High Order Components
Render Props
Render Props
Mesmos propósitos que HOCs
Mais controle sobre onde é aplicado (render)
Não interferem em statics/refs
Exemplo
Nova buzzword,
React.createContext *
Você ainda vai ouvir muito!
Muitas libs dando suporte
Porém, não substitui HOC 100%
Quando não usar:
Só funciona no render? e Lifecycles?
Não ser dinâmico pode ter vantagens
Testes
Integração 👍🏽
Unitário: 👎🏽
Evite "render-props" hell
const Example = () => {
return (
<Theme>
{theme => (
<Counter>
{counter => (
<Toggle>
{toggle => (
<div style={{ color: theme === 'light' ? '#000' : '#fff' }}>
<span>{`Count: ${counter}`}</span>
<button onClick={toggle}>{'Toggle'}</button>
</div>
)}
</Toggle>
)}
</Counter>
)}
</Theme>
)
}
react-adopt
const Composed = adopt({
theme: <Theme />,
counter: <Counter />,
toggle: <Toggle />,
})
const Example = () => (
<Composed>
{({ theme, counter, toggle }) => (
<div style={{ color: theme === 'light' ? '#000' : '#fff' }}>
<span>{`Count: ${counter}`}</span>
<button onClick={toggle}>{'Toggle'}</button>
</div>
)}
</Composed>
);
Qual usar?
HOC?
Render Props?
Compound Components?
Quem tal: nenhum?
Toda abstração tem um custo
Lei do menos poderoso
"On the Spectrum of Abstraction"
by Cheng Lou
Tradução: Obrigado
Estruturando Componentes
By Luciano Lima
Estruturando Componentes
Meetup ReactSSA #5 - Composição de componentes voltada para maior reusabilidade
- 1,081