Jesús García
y temas adicionales
Cada vez más viable gracias a la nueva API de Context, los hooks - en particular useReducer y, en el futuro, suspense para data-fetching
Aparentemente es la dirección que está tomando React
describing immutable updates with a pure function and data — I think it’s as relevant as ever! Less confident about needing a library for this.
- Dan Abramov
Hacer React... reactivo
class ObservableTodoStore {
@observable todos = [];
@observable pendingRequests = 0;
constructor() {
mobx.autorun(() => console.log(this.report));
}
@computed get completedTodosCount() {
return this.todos.filter(
todo => todo.completed === true
).length;
}
@computed get report() {
if (this.todos.length === 0)
return "<none>";
return `Next todo: "${this.todos[0].task}". ` +
`Progress: ${this.completedTodosCount}/${this.todos.length}`;
}
addTodo(task) {
this.todos.push({
task: task,
completed: false,
assignee: null
});
}
}
const observableTodoStore = new ObservableTodoStore();
@observer
class TodoList extends React.Component {
render() {
const store = this.props.store;
return (
<div>
{ store.report }
<ul>
{ store.todos.map(
(todo, idx) => <TodoView todo={ todo } key={ idx } />
) }
</ul>
{ store.pendingRequests > 0 ? <marquee>Loading...</marquee> : null }
<button onClick={ this.onNewTodo }>New Todo</button>
<small> (double-click a todo to edit)</small>
<RenderCounter />
</div>
);
}
onNewTodo = () => {
this.props.store.addTodo(prompt('Enter a new todo:','coffee plz'));
}
}
@observer
class TodoView extends React.Component {
render() {
const todo = this.props.todo;
return (
<li onDoubleClick={ this.onRename }>
<input
type='checkbox'
checked={ todo.completed }
onChange={ this.onToggleCompleted }
/>
{ todo.task }
{ todo.assignee
? <small>{ todo.assignee.name }</small>
: null
}
<RenderCounter />
</li>
);
}
onToggleCompleted = () => {
const todo = this.props.todo;
todo.completed = !todo.completed;
}
onRename = () => {
const todo = this.props.todo;
todo.task = prompt('Task name', todo.task) || todo.task;
}
}
ReactDOM.render(
<TodoList store={ observableTodoStore } />,
document.getElementById('reactjs-app')
);
/*<==<==<==<==<==<==<==<==<==<==<==<==<==<==<==<==<==<==\
⇓ ⇑
⇓ ⇑
⇓ ⇑
Middlewares ⇑*/
const middleware1 = store => next => action => {
/* Api <==<==<==<==<==<==<==<== Side effects */
console.log(action)
next(action)
}
const middleware2 = store => next => action => {
const {getState, dispatch} = store
/* Socket <==<==<==<==<==<==<== Side effects */
next(action)
}
const middleware3 = store => next => action => {
/* Analytics <==<==<==<==<==<== Side effects */
next(action)
}
/*⇓ ⇑
⇓ ⇑
⇓ ⇑
⇓ ⇑
Reducer */
const reducer = (state, action) => {
switch (action.type) {
case 'updateMovidas':
return {
...state,
mis: {
...state.mis,
movidas: action.payload
}
}
case 'otherAction':
/* return new state */
default:
return state;
}
}
/*⇓ ⇑
⇓ ⇑
⇓ ⇑
(Middleware de nuevo) ⇑
⇓ ⇑
⇓ ⇑
⇓ ⇑
Estado */
const state =
{
mis: {
movidas: 'guays'
},
}
/* <==<==<==<==<==<==<==<==<==<==<==/
⇓ ⇑
⇓ ⇑
⇓ ⇑
⇓ ⇑
⇓ ⇑
⇓ ⇑
⇓ ⇑
⇓ Selectores Acciones
*/ const selector = state => state.mis.movidas; const action = {
type: 'updateMovidas',
payload: 'fenomenal'
}
/* ⇑
⇓ ⇑
⇓ ⇑
⇓ ⇑
⇓ ⇑
*/
const actionCreator = () => ({
type: 'updateMovidas',
payload: 'fenomenal'
});
connect(
/*⇓ mapStateToProps ⇓*/
state => ({movidas: selector(state)}),
/*⇓ mapDispatchToProps ⇓*/
dispatch => ({handler: () => dispatch(actionCreator())})
/* shortHand mapDispatchToProps
{handler: actionCreator}
*/
)(MyComponent)
/* ⇑
⇓ ⇑
⇓ ⇑
=> => => => => => => => => => => => => ⇓ / => => => => => => => => => => => => => => /
⇓ ⇑
⇓ ⇑
Props, handlers ⇓ ⇑ Acciones
⇓ ⇑
⇓ ⇑
Vista */
function MyComponent({movidas, handler}){
return (
<div>
Mis movidas:
{movidas}
<button onClick={handler}/>
<div/>
)
}
The Elm Architecture - inspiró Redux
Los límites de errores son componentes de React que capturan errores de JavaScript en cualquier parte de su árbol de componentes hijo, registran esos errores, y muestran una interfaz de repuesto en lugar del árbol de componentes que ha fallado. Los límites de errores capturan errores durante el renderizado, en métodos del ciclo de vida, y en constructores de todo el árbol bajo ellos.
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { error: null, errorInfo: null };
}
componentDidCatch(error, errorInfo) {
// Catch errors in any components below and re-render with error message
this.setState({
error: error,
errorInfo: errorInfo
})
// You can also log error messages to an error reporting service here
}
render() {
if (this.state.errorInfo) {
// Error path
return (
<div>
<h2>Something went wrong.</h2>
<details style={{ whiteSpace: 'pre-wrap' }}>
{this.state.error && this.state.error.toString()}
<br />
{this.state.errorInfo.componentStack}
</details>
</div>
);
}
// Normally, just render children
return this.props.children;
}
}
class BuggyCounter extends React.Component {
constructor(props) {
super(props);
this.state = { counter: 0 };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(({counter}) => ({
counter: counter + 1
}));
}
render() {
if (this.state.counter === 5) {
// Simulate a JS error
throw new Error('I crashed!');
}
return <h1 onClick={this.handleClick}>{this.state.counter}</h1>;
}
}
function App() {
return (
<div>
<p>
<b>
This is an example of error boundaries in React 16.
<br /><br />
Click on the numbers to increase the counters.
<br />
The counter is programmed to throw when it reaches 5. This simulates a JavaScript error in a component.
</b>
</p>
<hr />
<ErrorBoundary>
<p>These two counters are inside the same error boundary. If one crashes, the error boundary will replace both of them.</p>
<BuggyCounter />
<BuggyCounter />
</ErrorBoundary>
<hr />
<p>These two counters are each inside of their own error boundary. So if one crashes, the other is not affected.</p>
<ErrorBoundary><BuggyCounter /></ErrorBoundary>
<ErrorBoundary><BuggyCounter /></ErrorBoundary>
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
render() {
// React *no* crea un nuevo div, convierte el hijo en `domNode`.
// `domNode` es cualquier nodo DOM válido, independientemente de su ubicación en el DOM.
return ReactDOM.createPortal(
this.props.children,
domNode
);
}
Los portales permiten renderizar hijos en un nodo DOM que existe por fuera de la jerarquía del DOM del componente padre.