Hola Del 24 al 25 de septiembre, se celebró en Moscú una conferencia de desarrolladores front-end de HolyJs https://holyjs-moscow.ru/ . Llegamos a la conferencia con nuestro stand en el que realizamos un cuestionario. Hubo un cuestionario principal: 4 rondas de clasificación y 1 ronda final, en las que se jugaron el Apple Watch y los constructores de lego. Y por separado, realizamos una prueba de conocimiento de reacción.
Debajo de las tareas de análisis de análisis de gato en reaccionar. Las opciones correctas estarán ocultas debajo del spoiler, por lo que no solo puede leer el análisis, sino también comprobarse a sí mismo :)

Vamos!
Por conveniencia, agrupamos las preguntas en secciones:
Sección 1. Comprensión básica del funcionamiento de this.setState y actualización del ciclo de vida del componente:
Pregunta 1.
react-: 1) SetProps, SetState, ForceUpdate 2) ForceUpdate, SetState 3) ForceUpdate, SetState, Parent (re)render 4) ForceUpdate, SetState, directly call UpdateComponent
La respuesta3) ForceUpdate, SetState, padre (re) render
Pregunta 2.
, this.setState({}) react 1) , updating lifecycle 2) , 3) React "Object cannot be empty" 4) state
La respuesta1) El componente está marcado como sucio, la actualización del ciclo de vida se llama
Análisis de las preguntas 1 y 2.Para responder la pregunta, analizaremos 2 partes:
1) Solicitud de componente propio para el ciclo de actualización
2) Solicitud fuera del componente
El componente en sí tiene 2 formas de actualizarse:
1) this.setState y this.forceUpdate. En este caso, el componente se marcará como sucio y en la marca Reconciliación, si tiene prioridad para la representación, se iniciará un ciclo de actualización.
Dato interesante: this.setState({})
y this.forceUpdate
son diferentes. Cuando se this.setState({})
se llama al ciclo de actualización completo, a diferencia de this.forceUpdate
, cuando el ciclo de actualización comienza sin el método shouldComponentUpdate. Puede encontrar un ejemplo de this.setState({})
aquí: https://codesandbox.io/s/m5jz2701l9 (si reemplaza setState con forceUpdate en el ejemplo, puede ver cómo cambia el comportamiento de los componentes).
2) Cuando se vuelve a representar el elemento primario del componente, devuelve la parte vDOM, todos los elementos secundarios que deberán actualizarse, y también se los llamará un ciclo de vida de actualización completo. Se puede evitar un recuento completo del subárbol describiendo shouldComponentUpdate o definiendo el componente como PureComponent.
Pregunta 3
Component PureComponent (PC) 1) Component , Pure 2) PC SCU, shallowEqual props state 3) PC , store 4) PC shouldComponentUpdate
Respuesta y análisis2) La PC implementa la SCU, realiza accesorios y estados de ShallowEqual
Como discutimos anteriormente, cuando (re) renderice el padre, el subárbol completo se enviará al ciclo de vida de actualización. Imagine que ha actualizado el elemento raíz. En este caso, de acuerdo con el efecto de cadena, tendrá que actualizar casi todo el árbol de reacción. Para optimizar y no enviar actualizaciones innecesarias, en reaccionar hay un método shouldComponentUpdate
, que le permite devolver verdadero si el componente debe actualizarse, y falso de lo contrario. Para simplificar las comparaciones en reaccionar, puede heredar de PureComponent
para prepararse inmediatamente shouldComponentUpdate
, que compara por referencia (si se trata de tipos de objeto) o por valor (si se trata de tipos de valor) todos los accesorios y el estado que entran en el componente.
Pregunta 4.
this.setState(() => {}, () => {}) — setState? 1) set . updating 2) state 3) setState 1
Respuesta y análisis2) Se llamará a la segunda función después de actualizar el estado
Hay dos métodos en el ciclo de vida de React: componentDidMount
para el bucle de montaje y componentDidUpdate para la actualización, donde puede agregar algo de lógica después de actualizar el componente. Por ejemplo, haga una solicitud http, realice algunos cambios de estilo, obtenga las métricas de los elementos html y (por condición) haga setState. Si desea realizar alguna acción después de cambiar ciertos campos en el estado, en el método componentDidUpdate
tendrá que escribir una comparación:
componentDidUpdate(prevProp, prevState) { if (prevState.foo !== this.state.foo) { // do awesome things here } }
O puede hacerlo mediante setState:
setState( // set new foo {foo: 'baz'}, () => { // do awesome things here } );
Cada enfoque tiene pros y contras (por ejemplo, si cambia setState en varios lugares, puede ser más conveniente escribir una condición una vez).
Pregunta 5.
render: class A extends React.PureComponent { render() { console.log('render'); return <div /> } } function Test() { return <A foo='bar' onClick={() => console.log('foo')} /> } const rootElement = document.getElementById("root"); ReactDOM.render(<Test />, rootElement); setTimeout(() => ReactDOM.render(<Test />, rootElement)); 1) 1 2) 2 3) 3 4) 0
Pregunta 6.
render: class A extends React.PureComponent { render() { console.log('render'); return <div /> } } function Test() { return <A foo='bar' /> } const rootElement = document.getElementById("root"); ReactDOM.render(<Test />, rootElement); setTimeout(() => ReactDOM.render(<Test />, rootElement)); 1) 1 2) 2 3) 3 4) 0
Pregunta 7
render: class A extends React.PureComponent { componentDidMount() { console.log('render'); } render() { return <div /> } } const rootElement = document.getElementById("root"); ReactDOM.render(<A />, rootElement); setTimeout(() => ReactDOM.render(<A />, rootElement)); 1) 1 2) 2 3) 3 4) 0
Análisis de las preguntas 5-7Las preguntas 5–7 son necesarias para lo mismo: verificar la comprensión del trabajo de PureComponent
y la actualización de los componentes en la transferencia de accesorios. Si dentro del método de representación pasamos una devolución de llamada jsx, describiendo esto directamente en la función de representación:
render () { return <Button onClick={() => {}} />; }
Luego, cada render del padre actualizará el controlador de clics dado. Esto sucede porque cada render crea una nueva función con un enlace único, que cuando se compara en PureComponent mostrará que los nuevos accesorios no son iguales a los antiguos y que necesita actualizar el componente. En el caso de que todas las comprobaciones pasen y shouldComponentUpdate devuelva falso, la actualización no se produce.
Sección 2. Claves en React
Un análisis detallado de las claves que publicamos aquí: https://habr.com/company/hh/blog/352150/
Pregunta 1.
key, ? 1) key 2) updating lifecycle 3) key 4) reconciliation
Respuesta y análisis1) Elimine la instancia anterior y monte la nueva cuando cambie la clave
Sin usar la tecla, reaccionar comparará la lista de elementos en pares de arriba a abajo. Si usamos la clave, la comparación se realizará en la clave correspondiente. Si ha aparecido una nueva clave, dicho componente no se comparará con nadie y se creará inmediatamente desde cero.
Puede usar este método incluso si tenemos 1 elemento: podemos configurar <A key="1" />
, en el siguiente render especificamos <A key="2" />
y en este caso reaccionar borrará <A key="1" />
y cree desde cero <A key="2" />
.
Pregunta 2.
this.prop.key? 1) 2) 3) static getKey
Respuesta y análisis2) no
El componente puede aprender la clave de sus hijos, que se le pasaron como accesorio, pero no puede aprender acerca de su clave.
Pregunta 3.
render: class A extends React.PureComponent { componentDidMount() { console.log('render'); } render() { return <div /> } } const rootElement = document.getElementById("root"); ReactDOM.render(<A key='1' />, rootElement); setTimeout(() => ReactDOM.render(<A />, rootElement)); 1) 1 2) 2 3) 3 4) 0
Respuesta y análisis2) 2
Al cambiar la clave, el componente se volverá a crear, por lo que el renderizado se mostrará dos veces.
Sección 3. Preguntas sobre jsx
Pregunta 1.
. 1) prop / context 2) 3) setParentProps 4) static getParentRef
Respuesta y análisis1) Devolución de llamada en forma de prop / contexto
2) Eliminación de la capa del modelo y trabajo a través de ella.
Hay dos respuestas correctas. Elegir cualquiera de ellos en un cuestionario contará sus puntos. Esta pregunta es para el conocimiento del flujo de datos reaccionar. Los datos de arriba a abajo se distribuyen en forma de accesorios o contexto, pueden contener una devolución de llamada, que el componente a continuación puede llamar para afectar el estado del sistema.
Otro método que combina la eliminación del modelo, el contexto y el apoyo es, por ejemplo, la unión react-redux.
Esta biblioteca toma un modelo derivado de react (redux). Establece redux.store en el Proveedor, que en realidad establece la tienda en contexto. Luego, el desarrollador utiliza la conexión HOC, que entra en el contexto, se suscribe a los cambios de la tienda (store.subscribe) y, cuando la tienda cambia, vuelve a mapStateToProps
función mapStateToProps
. Si los datos han cambiado, los establece en accesorios para el objeto envuelto.
Al mismo tiempo, connect le permite especificar mapDispatchToProps
, donde el desarrollador especifica los actionCreators que deben pasarse al componente. Nosotros, a su vez, los recibimos desde el exterior (sin contexto), actionCreators
los actionCreators
a la tienda (los envolvemos en store.dispatch) y los pasamos como accesorios al componente envuelto.
Pregunta 2.
props jsx? 1) 2) children
Respuesta y análisis1) en cualquier
Puedes transferir a cualquiera. Por ejemplo:
<Button icon={<Icon kind='warning'/>}></Button>
Dibuja un botón con un ícono. Este enfoque es muy conveniente de usar para dejar al componente el derecho de controlar la ubicación de varios elementos relativos entre sí, en lugar de clasificar un accesorio de niños.
Sección 4. Comprensión avanzada setState
Aquí hay 3 preguntas muy relacionadas:
Pregunta 1.
this.state = {a: 'a'}; ... this.setState({a: 'b'}); this.setState({a: this.state.a + 1}) this.state? 1) {a: 'a1'} 2) {a: 'b1'} 3) 4) {a: 'a'}
La respuesta3) No hay suficientes datos
Pregunta 2.
this.state={a: 'a'} ... this.setState({a: 'b'}) this.setState(state => ({a: state.a + 1})) this.state? 1) {a: 'a1'} 2) {a: 'b1'} 3) 4) {a: 'ab1'}
Pregunta 3.
2 setState componentDidUpdate updating lifecycle 1) 1 2) 2 3) 3 4)
Análisis de las preguntas 1-3Todo el trabajo de setState se describe completamente aquí:
1) https://reactjs.org/docs/react-component.html#setstate
2) https://stackoverflow.com/questions/48563650/does-react-keep-the-order-for-state-updates/48610973#48610973
El hecho es que setState no ocurre sincrónicamente.
Y si hay varias llamadas a setState en una fila, dependiendo de si estamos dentro del método react-lifecycle, la función del controlador del evento react (onChange, onClick) o no, la ejecución de setState depende.
Dentro de los controladores de reacción, setState funciona en lotes (los cambios se implementan solo después de que las funciones definidas por el usuario en la pila de llamadas hayan terminado y entremos en las funciones que llamaron a nuestro controlador de eventos y métodos de ciclo de vida). Ruedan uno tras otro, por lo que si estamos dentro del controlador de reacción, obtenemos:
this.state = {a: 'a'}; // a: 'a' ... this.state.a // a: 'a' this.setState({a: 'b'}); // a: 'b' + . this.state.a // a: 'a' this.setState({a: this.state.a + 1}) // a: 'a1'
desde que ocurrieron los cambios batchevo.
Pero al mismo tiempo, si se llamó a setState fuera de los controladores de reacción:
this.state = {a: 'a'}; // a: 'a' ... this.state.a // a: 'a' this.setState({a: 'b'}); // a: 'b' + this.state.a // a: 'b' this.setState({a: this.state.a + 1}) // a: 'b1' +
Como en este caso, los cambios se realizarán por separado.
Sección 5. Redux
Pregunta 1.
action, () => {} ? 1) . action type 2) , action type 3) , middleware action 4) , dispatch
Respuesta y análisis3) Sí, debe definir un middleware personalizado para tal acción
Tome redux-thunk como el ejemplo más simple. Todo el middleware es un pequeño bloque de código:
https://github.com/reduxjs/redux-thunk/blob/master/src/index.js#L2-L9
return ({ dispatch, getState }) => next => action => { if (typeof action === 'function') { return action(dispatch, getState, extraArgument); } return next(action); };
¿Cómo funcionan los middleware?
Consiguen el control antes de que la acción llegue a la tienda. Por lo tanto, una acción que se ha sobrescrito primero pasará por la cadena de middleware.
Cada middleware acepta una instancia de la tienda, el siguiente método, que le permite reenviar aún más la acción y la acción misma.
Si el middleware procesa acciones personalizadas, como redux-thunk, si action es una función, no reenvía la acción más adelante, sino que la "ahoga", en lugar de llamar a la acción con los métodos de despacho y getState pasados allí.
¿Qué pasaría si redux-thunk hiciera lo siguiente para la acción, que es una función?
Antes de llamar a los reductores, la tienda verifica el tipo de acción. Debe cumplir las siguientes condiciones:
1) Debe ser un objeto
2) Debe tener un campo de tipo
3) El campo de tipo debe ser de tipo cadena
Si una de las condiciones no se cumple, redux arrojará un error.
Preguntas extra:
Pregunta adicional 1.
? class Todos extends React.Component { getSnapshotBeforeUpdate(prevProps, prevState) { return this.props.list.length - prevProps.list.length; } componentDidUpdate(a, b, c) { console.log(c); } ... } ReactDOM.render(<Todos list={['a','b']} />, app); setTimeout(() => ReactDOM.render(<Todos list={['a','b','a','b']} />, app), 0); a) 0 b) 1 c) 2 d) undefined
Respuesta y análisisc) 2
getSnapshotBeforeUpdate
es una función rara vez utilizada en react que le permite obtener una instantánea, que luego se pasará a componentDidUpdate. Este método es necesario para calcular previamente ciertos datos en función de los cuales puede realizar, por ejemplo, una solicitud de recuperación.
Pregunta adicional 2.
2,5 ? function Input() { const [text, setText] = useState("World!"); useEffect( () => { let id = setTimeout(() => { setText("Hello " + text); }, 1000); return () => { clearTimeout(id); }; }, [text] ); return ( <input value={text} onChange={e => { setText(e.target.value); }} /> ); } a) "World!" b) "Hello World!" c) "Hello Hello World!" d)
Esto ya es una cuestión de conocer nuevas características en reaccionar; no estaba en nuestro cuestionario. Probemos en los comentarios para describir en detalle el funcionamiento del código de la última pregunta :)