React
O que é?
Uma biblioteca para descrever interfaces de usuário que reagem a dados.
Características
- Declarativo
- Biblioteca, não framework
- Baseado em componentes
- Genérico: a DOM é apenas mais um deles
Histórico
- Primeira fase
- Web design que assume o tamanho da tela
- Sem linguagem script padrão para interatividade na web
- O ECMAScript nasce e tenta usar a popularidade do Java pra crescer, se tornando conhecido como Javascript
- Ainda assim, JS é pouco padronizado
- Além disso, JS é para manipulações simples
Histórico
- Segunda fase
- jQuery padroniza e facilita a manipulação da DOM
- jQuery explode em popularidade e leva consigo o JS
- Com o Bootstrap e o jQuery UI, tem-se a primeira 'componentização' da web
- JS deixa de ser usado apenas para animações ou interações simples, aplicações web crescem e jQuery começa a não ser suficiente
Histórico
- Terceira fase
- Nasce o Backbone, que abstrai completamente a manipulação da DOM de aplicações JS
- Popularizam-se as SPAs (Single Page Applications)
- JS explode, esforços são feitos para a padronização, nascem Node, Angular, React, Electron
- Novas versões do JS são lançadas regularmente
Porque não
jQuery?
Em suma:
- Baseado em DOM
- Paradigma imperativo
- Gestão de complexidade
Vamo brincar de benchmark? :D
class Tasks {
constructor() {
try {
this._tasks = JSON.parse(window.localStorage.getItem("tasks")) || [];
} catch (err) {
this._tasks = [];
}
}
get tasks() { return [ ...this._tasks ] };
set tasks(tasks) {
this.setTasks(tasks);
}
async setTasks(tasks) {
this._tasks = tasks;
window.localStorage.setItem("tasks", tasks);
const time = await updateUI(this._tasks, tasks);
return time;
}
async addTask(content) {
return await this.setTasks(this.tasks.concat({ id: uuid(), content, checked: false }));
}
}
const tasksManager = new Tasks();
function updateUI() {
$("#task-table tbody").empty();
for (const task of tasksManager.tasks) {
$("#task-table tbody").append(`
<tr>
<td><input type="checkbox" ${task.checked ? "checked": ""} id="checkbox-${task.id}"></td>
<td>${task.content}</td>
<td><button id="delete-${task.id}">Excluir</button></td>
</tr>
`);
$(`#checkbox-${task.id}`).click(() => checkTask(task.id));
$(`#delete-${task.id}`).click(() => deleteTask(task.id));
}
return new Promise((resolve) => setTimeout(resolve, 1));
}
function checkTask(id) {
const task = tasksManager.tasks.find(t => t.id === id);
task.checked = !task.checked;
tasksManager.tasks = tasksManager.tasks;
}
function deleteTask(id) {
tasksManager.tasks = tasksManager.tasks.filter(t => t.id !== id);
}
function addTask() {
const content = prompt("Que tarefa você deseja adicionar?");
if (content) {
tasksManager.addTask(content);
}
}
function logspace(min, max, steps) {
const range = Math.log(max) - Math.log(min);
return [...new Array(steps)].map((_, i) => Math.exp(Math.log(min) + i * range / (steps - 1)));
}
let logs = "";
function logTime(n) {
if (n) {
logs += n.toFixed(3).replace(".", ",") + "\n";
} else {
console.log(logs);
logs = "";
}
}
async function stressTest(addTask, clearTasks, betweenTests) {
const sizes = logspace(1, 100, 10).map(Math.round);
const testSampleSize = 3;
const beforeAll = window.performance.now();
const times = [];
for (const size of sizes) {
const before = window.performance.now();
for (const _ of arr(testSampleSize)) {
clearTasks();
for (const __ of arr(size)) {
await addTask(uuid());
}
}
times.push(window.performance.now() - before);
betweenTests && betweenTests();
}
const results = sizes.map((s, i) => ({[s]: times[i]})).reduce((acc, v) => Object.assign(acc, v), {});
const means = sizes.map((s, i) => ({[s]: times[i] / testSampleSize / s})).reduce((acc, v) => Object.assign(acc, v), {});
const totalTime = window.performance.now() - beforeAll;
const mean = totalTime / testSampleSize / sizes.reduce((acc, v) => acc + v, 0);
console.log("sizes");
sizes.forEach(s => (logTime(s), logTime(s)));
logTime(null);
console.log("=====");
logTime(totalTime);
logTime(mean);
logTime(null);
for (const size of sizes) {
// console.log("size " + size);
logTime(results[size]);
logTime(means[size]);
}
logTime(null);
};
function arr(n) {
const arr = [];
for (let i = 0; i < n; i++) {
arr.push(i);
}
return arr;
}
jQuery.fn.flush = function () { jQuery(this.context).find(this.selector); }
$(() => {
$("#add-task-button").click(addTask);
$("#stress-test").click(() => stressTest(
() => tasksManager.addTask(uuid()),
() => tasksManager.tasks = [],
() => $("body").flush(),
));
});
function arr(n) {
const arr = [];
for (let i = 0; i < n; i++) {
arr.push(i);
}
return arr;
}
function logspace(min, max, steps) {
const range = Math.log(max) - Math.log(min);
return arr(steps).map((_, i) => Math.exp(Math.log(min) + i * range / (steps - 1)));
}
let logs = "";
function logTime(n) {
if (n) {
logs += n.toFixed(3).replace(".", ",") + "\n";
} else {
console.log(logs);
logs = "";
}
}
async function stressTest(addTask, clearTasks) {
const sizes = logspace(1, 100, 10).map(Math.round);
const testSampleSize = 2E1;
const beforeAll = window.performance.now();
const times = [];
for (const size of sizes) {
const subTimes = [];
const before = window.performance.now();
for (const _ of arr(testSampleSize)) {
clearTasks();
let time = 0;
for (const __ of arr(size)) {
await addTask(uuid());
}
subTimes.push(time);
}
times.push(window.performance.now() - before);
}
const results = sizes.map((s, i) => ({[s]: times[i]})).reduce((acc, v) => Object.assign(acc, v), {});
const means = sizes.map((s, i) => ({[s]: times[i] / testSampleSize / s})).reduce((acc, v) => Object.assign(acc, v), {});
const totalTime = window.performance.now() - beforeAll;
const mean = totalTime / testSampleSize / sizes.reduce((acc, v) => acc + v, 0);
console.log("sizes");
sizes.forEach(s => (logTime(s), logTime(s)));
logTime(null);
console.log("=====");
logTime(totalTime);
logTime(mean);
logTime(null);
for (const size of sizes) {
// console.log("size " + size);
logTime(results[size]);
logTime(means[size]);
}
logTime(null);
};
class Todo extends React.Component {
constructor(props, context) {
super(props, context);
this.state = { tasks: [] };
}
addTask(content) {
return new Promise((resolve) => {
const before = window.performance.now();
this.setState({
tasks: (this.state.tasks || []).concat({
id: uuid(),
content,
checked: false,
}),
}, () => setTimeout(() => resolve(window.performance.now() - before)), 1);
});
}
deleteTask(id) {
this.setState({
tasks: (this.state.tasks || []).filter(t => t.id !== id),
});
}
checkTask(id) {
this.setState({
tasks: (this.state.tasks || []).map(t => {
if (t.id !== id) return t;
else return { ...t, checked: !t.checked };
}),
});
}
clearTasks() {
this.setState({ tasks: [] });
}
render() {
return (
<div>
<button id="stress-test" onClick={() => stressTest(this.addTask.bind(this), this.clearTasks.bind(this))}>
Teste de estresse
</button>
<div className="root">
<button id="add-task-button" onClick={() => this.addTask(prompt("Digite a tarefa a ser inserida"))}>
Adicionar tarefa
</button>
<table id="task-table">
<thead>
<tr>
<th>Feito</th>
<th>Nome</th>
<th></th>
</tr>
</thead>
<tbody>
{
this.state.tasks.map(task => (
<tr>
<td>
<input type="checkbox" onChange={
() => this.checkTask(task.id)
}/>
</td>
<td>{task.content}</td>
<td>
<button onClick={
() => this.deleteTask(task.id)
}>
Deletar
</button>
</td>
</tr>
))
}
</tbody>
</table>
</div>
</div>
);
}
}
ReactDOM.render(<Todo/>, document.querySelector("body"));
Performance

Performance

Performance

Algoritmo
Com o jQuery, toda mudança é feita diretamente na DOM, cuja manipulação é custosa. Além disso, existem memory leaks conhecidos.
Já o React faz as manipulações numa estrutura de dados que representa a DOM (a Virtual DOM), determina o que foi modificado e reconcilia o layout com sua reconciliação com o mínimo possível de operações.
Algoritmo

JSX
e Virtual DOM
Targets
- React implementa uma estrutura de dados feita para descrever interfaces de usuário
- Portanto, para transformar a descrição em layout, precisa-se de alguma outra biblioteca. Exemplos:
- HTML: ReactDOM
- Sketch: ReactSketchApp
- Mobile: ReactNative
- Realidade Virtual: ReactVR
- Até windows!
Propriedades
e Estado
Ciclo
de Vida
React
By Victor Magalhães
React
- 313