Slide 1

Slide 1 text

Introdução ao React Gabriel Sobrinho

Slide 2

Slide 2 text

hite.com.br

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

Conceitos

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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" );

Slide 8

Slide 8 text

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);

Slide 9

Slide 9 text

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; }

Slide 10

Slide 10 text

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"] ]

Slide 11

Slide 11 text

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);

Slide 12

Slide 12 text

Virtual DOM delete n1.id; n1.className = "success"; n1.role = "message"; n1.innerHTML = "World";

Slide 13

Slide 13 text

Virtual DOM • Representação virtual do DOM • Renderizada a representação • Computada a diferença • Aplicado o menor patch possível

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

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)

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

JSX Hite = React.createClass({ render: function () { return ( www.hite.com.br ); } });

Slide 18

Slide 18 text

JSX • Infere automaticamente o displayName • Trabalha com componentes em namespace • Spread operator (similar ao splat operator do ruby)

Slide 19

Slide 19 text

No content

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

Componentes React.render( , document.getElementById('example') );

Slide 22

Slide 22 text

Componentes var Avatar = React.createClass({ render: function() { return (
); } });

Slide 23

Slide 23 text

Componentes var ProfilePic = React.createClass({ render: function() { var src = 'https://graph.facebook.com/' + this.props.username + '/picture'; return ( ); } });

Slide 24

Slide 24 text

Componentes var ProfileLink = React.createClass({ render: function() { var href = 'https://www.facebook.com/' + this.props.username; return ( {this.props.username} ); } });

Slide 25

Slide 25 text

Componentes

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

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)

Slide 28

Slide 28 text

Props var Gravatar = React.createClass({ render: function() { var name = this.props.name, src = 'https://gravatar.com/ avatar/' + md5(this.props.email); return ( {name} ); } });

Slide 29

Slide 29 text

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 ( {name} ); } });

Slide 30

Slide 30 text

No content

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

{count}

; }, increment: function() { this.setState({count: this.state.count + 1}); } });

Slide 33

Slide 33 text

No content

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

Hooks componentWillMount: function() { this.$ = jQuery; }

Slide 36

Slide 36 text

Hooks componentDidMount: function() { this.interval = setInterval(this.increment, 1000); } componentWillUnmount: function() { clearInterval(this.interval); }

Slide 37

Slide 37 text

Hooks componentWillReceiveProps: function(nextProps) { this.setState({ likesIncreasing: nextProps.likeCount > this.props.likeCount }); }

Slide 38

Slide 38 text

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 } }

Slide 39

Slide 39 text

No content

Slide 40

Slide 40 text

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 ¯\_(ツ)_/¯

Slide 41

Slide 41 text

Mixins componentDidMount: function() { this.interval = setInterval(this.increment, 1000); } componentWillUnmount: function() { clearInterval(this.interval); }

Slide 42

Slide 42 text

Mixins componentDidMount: function() { if (this.props.autoIncrement) { this.interval = setInterval(this.increment, 1000); } } componentWillUnmount: function() { if (this.interval) { clearInterval(this.interval); } }

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

Mixins SetIntervalMixin = { componentWillMount: function() { this.intervals = []; }, setInterval: function() { this.intervals.push(setInterval.apply(null, arguments)); }, componentWillUnmount: function() { this.intervals.map(clearInterval); } };

Slide 45

Slide 45 text

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

{count}

; }, increment: function() { this.setState({count: this.state.count + 1}); } });

Slide 46

Slide 46 text

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

{count}

; }, increment: function() { this.setState({count: this.state.count + 1}); } });

Slide 47

Slide 47 text

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 } } };

Slide 48

Slide 48 text

No content

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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 ; } });

Slide 51

Slide 51 text

Formulários var WithLink = React.createClass({ mixins: [React.addons.LinkedStateMixin], getInitialState: function() { return { message: 'Hello!' }; }, render: function() { return ; } });

Slide 52

Slide 52 text

No content

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

Flux

Slide 55

Slide 55 text

Flux

Slide 56

Slide 56 text

Flux • Facebook: https://github.com/facebook/flux • Yahoo: http://fluxible.io • Josh Perez: http://alt.js.org • Hite: https://github.com/samuelsimoes/fluxo

Slide 57

Slide 57 text

No content

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

Fluxo

Slide 60

Slide 60 text

Fluxo Schedule = Fluxo.Store.extend({ computed: { date: ["change:at"] }, date: function () { return this.data.at.split("T")[0]; } });

Slide 61

Slide 61 text

Fluxo schedule = Schedule.new({ id: 1, at: "2015-07-04T04:00:00.000Z", professional_id: 1, professional_name: "John" });

Slide 62

Slide 62 text

Fluxo schedule.toJSON() { id: 1, at: "2015-07-04T04:00:00.000Z", professional_id: 1, professional_name: "John", date: "2015-07-04" }

Slide 63

Slide 63 text

Fluxo Schedules = Fluxo.CollectionStore.extend({ store: Schedule, computed: { dates: ["add", "remove"] }, dates: function () { return this.map(function (store) { return store.get("date"); }); } });

Slide 64

Slide 64 text

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" } ]);

Slide 65

Slide 65 text

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" } ] }

Slide 66

Slide 66 text

Fluxo var schedules = new Schedules(window.schedulesData); var MedicalReferralComponent = FluxoReactConnectStores(MedicalReferralComponent, { schedules: schedules }); React.render( React.createElement(MedicalReferralComponent), document.getElementById("app-container") );

Slide 67

Slide 67 text

Fluxo Publicado em https://github.com/samuelsimoes/fluxo

Slide 68

Slide 68 text

No content

Slide 69

Slide 69 text

Dúvidas?

Slide 70

Slide 70 text

Obrigado!

Slide 71

Slide 71 text

Referências • https://facebook.github.io/react/index.html • https://github.com/Matt-Esch/virtual-dom • https://pt.wikipedia.org/wiki/Cross-cutting_concern • http://blog.vjeux.com/2013/javascript/scroll-position- with-react.html • http://blog.andrewray.me/flux-for-stupid-people/ • https://facebook.github.io/flux/