Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Introdução ao React

Introdução ao React

Gabriel Sobrinho

August 08, 2015
Tweet

More Decks by Gabriel Sobrinho

Other Decks in Programming

Transcript

  1. React • Biblioteca para construir interfaces de usuário • Implementa

    apenas a camada de view • Trabalha com Virtual DOM para ser performático • Fluxo de dados em direção única • Desenvolvido e mantido pelo Facebook/Instagram
  2. Virtual DOM • Representação em memória dos nodes usando objetos

    puros • Em tese pode ser implementado adapter para qualquer client (browser, mobile, ascii, etc) • Permite computar o diff de diferentes momentos ou nodes • Com o diff computado é possível realizar um patch no renderizado com o menor impacto possível
  3. Virtual DOM function VNode (name, attributes, content) { this.name =

    name; this.attributes = attributes; this.content = content; } vn1 = new VNode( "p", { className: "failure", id: "failure" }, "Hello" ); vn2 = new VNode( "p", { className: "success", role: "message" }, "World" );
  4. Virtual DOM function render (vn) { var attr, n =

    document.createElement(vn.name); for (attr in vn.attributes) { n[attr] = vn.attributes[attr]; } n.innerHTML = vn.content; return n; } n1 = render(vn1); document.getElementById("app").appendChild(n1);
  5. Virtual DOM function diff (vn1, vn2) { var attr, diff

    = []; for (attr in vn1.attributes) { if (!(attr in vn2.attributes)) { diff.push([attr, undefined]); } } for (attr in vn2.attributes) { if (vn1.attributes[attr] !== vn2.attributes[attr]) { diff.push([attr, vn2.attributes[attr]]); } } if (vn1.content != vn2.content) { diff.push(["innerHTML", vn2.content]); } return diff; }
  6. Virtual DOM vn1 = new VNode( "p", { className: "failure",

    id: "failure" }, "Hello" ); vn2 = new VNode( "p", { className: "success", role: "message" }, "World" ); diff = diff(vn1, vn2); [ ["id", undefined], ["className", "success"], ["role", "message"], ["innerHTML", "World"] ]
  7. Virtual DOM function patch (n, diff) { var idx, pair;

    for (idx in diff) { pair = diff[idx]; if (pair[1]) { n[pair[0]] = pair[1]; } else { delete n[pair[0]]; } } } patch(n1, diff);
  8. Virtual DOM • Representação virtual do DOM • Renderizada a

    representação • Computada a diferença • Aplicado o menor patch possível
  9. JSX • Transpiler de XML para JavaScript • Uso opcional

    mas recomendado • Elimina a necessidade de conhecer a API da biblioteca vDOM utilizada pelo React • Facilita manutenção dos componentes por desenvolvedores casuais (designers e etc)
  10. JSX Hite = React.createClass({ displayName: "Hite", render: function () {

    return React.createElement( "a", { href: "http://www.hite.com.br/" }, "Hite" ); } });
  11. JSX Hite = React.createClass({ render: function () { return (

    <a href="http://www.hite.com.br"> www.hite.com.br </a> ); } });
  12. JSX • Infere automaticamente o displayName • Trabalha com componentes

    em namespace • Spread operator (similar ao splat operator do ruby)
  13. Componentes • React se resume basicamente apenas a construir componentes

    • Não possui nenhuma opinião sobre como a arquitetura da sua aplicação deve ser feita (é uma biblioteca e não um framework) • Componentes são bem definidos, previsíveis, encapsulados, testáveis e re-usáveis
  14. Componentes var Avatar = React.createClass({ render: function() { return (

    <div> <ProfilePic username={this.props.username} /> <ProfileLink username={this.props.username} /> </div> ); } });
  15. Componentes var ProfilePic = React.createClass({ render: function() { var src

    = 'https://graph.facebook.com/' + this.props.username + '/picture'; return ( <img src={src} /> ); } });
  16. Componentes var ProfileLink = React.createClass({ render: function() { var href

    = 'https://www.facebook.com/' + this.props.username; return ( <a href={href}> {this.props.username} </a> ); } });
  17. Props • Armazena argumentos externos do componente • Somente leitura

    • Pode ser qualquer valor, desde primitivos a objetos complexos • IMO deve conter apenas primitivos para facilitar o cálculo de rendering • Permite validar o valor recebido, inclusive obrigatoriedade (serve para documentar e pseudo tipagem estática)
  18. Props var Gravatar = React.createClass({ render: function() { var name

    = this.props.name, src = 'https://gravatar.com/ avatar/' + md5(this.props.email); return ( <img alt={name} src={src} /> ); } });
  19. Props var Gravatar = React.createClass({ propTypes: { name: React.PropTypes.string, email:

    React.PropTypes.string.isRequired, }, render: function() { var name = this.props.name, src = 'https://gravatar.com/avatar/' + md5(this.props.email); return ( <img alt={name} src={src} /> ); } });
  20. State • Armazena o estado interno do componente • Permite

    leitura e escrita • O componente é automaticamente atualizado no DOM quando uma alteração é escrita • Deve ser evitado sempre que possível (ainda falaremos sobre o Flux para evitar isso) • Não permite validação de tipos já que se trata de dados internos do próprio componente • IMO deveria permitir já que podemos confundir o tipo do dado e causar um comportamento inesperado
  21. State var Counter = React.createClass({ getInitialState: function() { return {

    count: 0 }; }, componentDidMount: function() { this.interval = setInterval(this.increment, 1000); }, componentWillUnmount: function() { clearInterval(this.interval); }, render: function() { var count = this.state.count; return <p>{count}</p>; }, increment: function() { this.setState({count: this.state.count + 1}); } });
  22. Hooks • componentWillMount
 Executado antes do componente ser renderizado no

    DOM • componentDidMount
 Executado após o componente ser renderizado no DOM • componentWillReceiveProps
 Executado antes do componente receber novas propriedades • componentWillUpdate
 Executado antes do componente ser atualizado (props e/ou state) • componentDidUpdate
 Executado após o componente ser atualizado (props e/ou state) • componentWillUnmount
 Executado antes do componente ser removido do DOM
  23. Hooks componentWillUpdate: function() { var node = this.getDOMNode(); this.shouldScrollBottom =

    node.scrollTop + node.offsetHeight === node.scrollHeight; } componentDidUpdate: function() { if (this.shouldScrollBottom) { var node = this.getDOMNode(); node.scrollTop = node.scrollHeight } }
  24. Mixins • Proposta nativa para implementar reuso de código entre

    diferentes componentes • Algumas vezes chamado de cross-cutting concern, interesses transversais ou características transversais • Ainda funciona mas foi deprecado com a entrada do uso do ES6 mas pode ser substituído pelo cocktail ou composition • Prefiro chamar apenas de mixin ¯\_(ツ)_/¯
  25. Mixins componentDidMount: function() { if (this.props.autoIncrement) { this.interval = setInterval(this.increment,

    1000); } } componentWillUnmount: function() { if (this.interval) { clearInterval(this.interval); } }
  26. Mixins componentDidMount: function() { this.incrementAInterval = setInterval(this.incrementA, 1000); this.incrementBInterval =

    setInterval(this.incrementB, 1000); } componentWillUnmount: function() { clearInterval(this.incrementAInterval); clearInterval(this.incrementBInterval); }
  27. Mixins SetIntervalMixin = { componentWillMount: function() { this.intervals = [];

    }, setInterval: function() { this.intervals.push(setInterval.apply(null, arguments)); }, componentWillUnmount: function() { this.intervals.map(clearInterval); } };
  28. Mixins var Counter = React.createClass({ getInitialState: function() { return {

    count: 0 }; }, componentDidMount: function() { this.interval = setInterval(this.increment, 1000); }, componentWillUnmount: function() { clearInterval(this.interval); }, render: function() { var count = this.state.count; return <p>{count}</p>; }, increment: function() { this.setState({count: this.state.count + 1}); } });
  29. Mixins var Counter = React.createClass({ mixins: [SetIntervalMixin], getInitialState: function() {

    return { count: 0 }; }, componentDidMount: function() { this.setInterval(this.increment, 1000); }, render: function() { var count = this.state.count; return <p>{count}</p>; }, increment: function() { this.setState({count: this.state.count + 1}); } });
  30. Mixins ScrollToBottomMixin = { componentWillUpdate: function() { var node =

    this.getDOMNode(); this.shouldScrollBottom = node.scrollTop + node.offsetHeight === node.scrollHeight; }, componentDidUpdate: function() { if (this.shouldScrollBottom) { var node = this.getDOMNode(); node.scrollTop = node.scrollHeight } } };
  31. Formulários • input, textarea, select recebem iteração do usuário que

    mutam o estado do input (input.value e input.checked) • Podemos escutar esses eventos para reagir conforme necessário • Por padrão o React mantém o input atualizado com o valor informado pelo componente • React já possui um mixin para 2-way binding
  32. Formulários var NoLink = React.createClass({ getInitialState: function() { return {message:

    'Hello!'}; }, handleChange: function(event) { this.setState({message: event.target.value}); }, render: function() { var message = this.state.message; return <input type="text" value={message} onChange={this.handleChange} />; } });
  33. Formulários var WithLink = React.createClass({ mixins: [React.addons.LinkedStateMixin], getInitialState: function() {

    return { message: 'Hello!' }; }, render: function() { return <input type="text" valueLink={this.linkState('message')} />; } });
  34. Flux • Arquitetura proposta pelo Facebook para criar aplicações client-side

    • É uma arquitetura e não uma implementação • Bastante similar a proposta original do MVC • Já existem várias implementações e quase todas diferem da proposta original • Ainda existem questões que não foram especificadas pelo Flux
  35. Flux • Facebook: https://github.com/facebook/flux • Yahoo: http://fluxible.io • Josh Perez:

    http://alt.js.org • Hite: https://github.com/samuelsimoes/fluxo
  36. Fluxo • Implementação da arquitetura Flux pela Hite • A

    diferença é que não temos o Dispatcher • Actions conversam diretamente com os Stores (ainda não sabemos se isso é bom ou ruim) • Inspirado nas boas idéias do Backbone Model, Backbone Collection, Ember e Flux • Já estamos utilizando em produção
  37. Fluxo Schedule = Fluxo.Store.extend({ computed: { date: ["change:at"] }, date:

    function () { return this.data.at.split("T")[0]; } });
  38. Fluxo Schedules = Fluxo.CollectionStore.extend({ store: Schedule, computed: { dates: ["add",

    "remove"] }, dates: function () { return this.map(function (store) { return store.get("date"); }); } });
  39. Fluxo schedules = Schedules.new([ { id: 1, at: "2015-07-04T04:00:00.000Z", professional_id:

    1, professional_name: "John" }, { id: 3, at: "2015-07-05T04:00:00.000Z", professional_id: 1, professional_name: "John" } ]);
  40. Fluxo schedules.toJSON() { data: { dates: ["2015-07-04", "2015-07-05"], }, stores:

    [ { id: 1, at: "2015-07-04T04:00:00.000Z", professional_id: 1, professional_name: "John", date: "2015-07-04" }, { id: 3, at: "2015-07-05T04:00:00.000Z", professional_id: 1, professional_name: "John", date: "2015-07-05" } ] }
  41. Fluxo var schedules = new Schedules(window.schedulesData); var MedicalReferralComponent = FluxoReactConnectStores(MedicalReferralComponent,

    { schedules: schedules }); React.render( React.createElement(MedicalReferralComponent), document.getElementById("app-container") );