Oi De 24 a 25 de setembro, foi realizada em Moscou uma conferência dos desenvolvedores de front-end do HolyJs, https://holyjs-moscow.ru/ . Chegamos à conferência com o estande em que realizamos o questionário. Houve um quiz principal - 4 rodadas de qualificação e 1 rodada final, nas quais os construtores Apple Watch e lego foram jogados. E separadamente, realizamos um teste de conhecimento sobre reação.
Em tarefas de teste de análise de gato em reagir. As opções corretas estarão ocultas no spoiler, para que você possa não apenas ler a análise, mas também verificar a si mesmo :)

Vamos lá!
Por conveniência, agrupamos as perguntas em seções:
Seção 1. Entendimento básico da operação deste ciclo de vida do componente this.setState e atualização:
Pergunta 1.
react-: 1) SetProps, SetState, ForceUpdate 2) ForceUpdate, SetState 3) ForceUpdate, SetState, Parent (re)render 4) ForceUpdate, SetState, directly call UpdateComponent
A resposta3) ForceUpdate, SetState, Parent (re) render
Questão 2.
, this.setState({}) react 1) , updating lifecycle 2) , 3) React "Object cannot be empty" 4) state
A resposta1) O componente está marcado como sujo, o ciclo de vida da atualização é chamado
Análise das questões 1 e 2Para responder à pergunta, analisaremos duas partes:
1) Solicitação de componente próprio para o ciclo de atualização
2) Solicitação fora do componente
O componente em si tem 2 maneiras de se atualizar:
1) this.setState e this.forceUpdate. Nesse caso, o componente será marcado como sujo e, no campo Reconciliação, se for prioritário para renderização, um ciclo de atualização será iniciado.
Fato interessante: this.setState({})
e this.forceUpdate
são diferentes. Quando this.setState({})
é chamado, o loop de atualização completo é chamado, diferentemente de this.forceUpdate
, quando o loop de atualização é iniciado sem o método shouldComponentUpdate. Um exemplo do trabalho de this.setState({})
pode ser encontrado aqui: https://codesandbox.io/s/m5jz2701l9 (se você substituir setState por forceUpdate no exemplo, poderá ver como o comportamento dos componentes é alterado).
2) Quando o pai do componente é renderizado novamente, ele retorna a parte do vDOM, todos os filhos que precisarão ser atualizados e também serão chamados de ciclo de vida completo da atualização. Uma recontagem completa da subárvore pode ser evitada descrevendo shouldComponentUpdate ou definindo o componente como PureComponent.
Pergunta 3
Component PureComponent (PC) 1) Component , Pure 2) PC SCU, shallowEqual props state 3) PC , store 4) PC shouldComponentUpdate
Resposta e análise2) O PC implementa a SCU, realiza adereços rasos e iguais e declara
Como discutimos anteriormente, ao (re) renderizar o pai, toda a subárvore será enviada para o ciclo de vida da atualização. Imagine que você atualizou o elemento raiz. Nesse caso, de acordo com o efeito em cadeia, você terá que atualizar quase toda a árvore de reação. Para otimizar e não enviar atualizações desnecessárias, em reagir, existe um método shouldComponentUpdate
, que permite retornar true se o componente deve ser atualizado e false caso contrário. Para simplificar as comparações ao reagir, você pode herdar do PureComponent
para se preparar imediatamente shouldComponentUpdate
, que é comparado por referência (se for para tipos de objeto) ou por valor (se for sobre tipos de valor) todos os adereços e estados que entram no componente.
Pergunta 4.
this.setState(() => {}, () => {}) — setState? 1) set . updating 2) state 3) setState 1
Resposta e análise2) A segunda função será chamada após a atualização do estado
Existem dois métodos no ciclo de vida do React: componentDidMount
para o loop de montagem e componentDidUpdate para atualização, em que você pode adicionar alguma lógica após atualizar o componente. Por exemplo, faça uma solicitação http, faça algumas mudanças de estilo, obtenha as métricas dos elementos html e (por condição) faça setState. Se você deseja executar alguma ação após alterar determinados campos no estado, no método componentDidUpdate
você precisará escrever uma comparação:
componentDidUpdate(prevProp, prevState) { if (prevState.foo !== this.state.foo) { // do awesome things here } }
Ou você pode fazer isso por setState:
setState( // set new foo {foo: 'baz'}, () => { // do awesome things here } );
Cada abordagem tem prós e contras (por exemplo, se você alterar setState em vários locais, pode ser mais conveniente escrever uma condição uma vez).
Pergunta 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
Pergunta 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
Pergunta 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álise das questões 5-7As perguntas 5–7 são necessárias para a mesma coisa - verificar o entendimento do trabalho do PureComponent
e a atualização dos componentes no momento da transferência dos adereços. Se dentro do método render passarmos um retorno de chamada jsx, descrevendo isso diretamente na função render:
render () { return <Button onClick={() => {}} />; }
Cada renderização do pai atualizará o manipulador de cliques fornecido. Isso acontece porque cada renderização cria uma nova função com um link exclusivo, que quando comparado no PureComponent mostra que os novos adereços não são iguais aos antigos e você precisa atualizar o componente. No caso em que todas as verificações passam e shouldComponentUpdate retorna false, a atualização não ocorre.
Seção 2. Chaves no React
Uma análise detalhada das chaves que publicamos aqui: https://habr.com/company/hh/blog/352150/
Pergunta 1.
key, ? 1) key 2) updating lifecycle 3) key 4) reconciliation
Resposta e análise1) Exclua a instância anterior e monte a nova ao alterar a chave
Sem usar a tecla, o reagir comparará a lista de elementos em pares de cima para baixo. Se usarmos a chave, a comparação ocorrerá na chave correspondente. Se uma nova chave aparecer, esse componente não será comparado a ninguém e será criado imediatamente do zero.
Você pode usar esse método mesmo se tivermos 1 elemento: podemos definir <A key="1" />
, na próxima renderização, especificaremos <A key="2" />
e, nesse caso, o <A key="2" />
excluirá <A key="1" />
e crie a partir do zero <A key="2" />
.
Questão 2.
this.prop.key? 1) 2) 3) static getKey
Resposta e análise2) Não
O componente pode aprender a chave de seus filhos, que foram passados para ele como suporte, mas não pode aprender sobre sua chave.
Pergunta 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
Resposta e análise2) 2
Ao alterar a chave, o componente será recriado, portanto a renderização será exibida duas vezes.
Seção 3. Perguntas sobre jsx
Pergunta 1.
. 1) prop / context 2) 3) setParentProps 4) static getParentRef
Resposta e análise1) Retorno de chamada na forma de prop / context
2) Remoção da camada do modelo e trabalho através dele
Existem duas respostas corretas. Escolher qualquer um deles em um teste contará pontos. Esta questão é para o conhecimento do fluxo de dados reagir. Os dados de cima para baixo são distribuídos na forma de adereços ou contexto; podem conter um retorno de chamada, que o componente abaixo pode chamar para afetar o estado do sistema.
Outro método que combina remoção de modelo, contexto e suporte é, por exemplo, a ligação react-redux.
Esta biblioteca usa um modelo derivado de react (redux). Define redux.store no provedor, que realmente define o armazenamento no contexto. Em seguida, o desenvolvedor usa a conexão HOC, que entra no contexto, assina as alterações na loja (store.subscribe) e, quando a loja é alterada, reconta a função mapStateToProps
. Se os dados foram alterados, eles os definem em adereços para o objeto empacotado.
Ao mesmo tempo, o connect permite especificar mapDispatchToProps
, em que o desenvolvedor especifica os actionCreators que devem ser passados para o componente. Por sua vez, nós os recebemos de fora (sem contexto), vinculamos os actionCreators
à loja (envolva-os em store.dispatch) e os passamos como acessórios para o componente actionCreators
.
Questão 2.
props jsx? 1) 2) children
Resposta e análise1) Em qualquer
Você pode transferir para qualquer um. Por exemplo:
<Button icon={<Icon kind='warning'/>}></Button>
Desenha um botão com um ícone. Essa abordagem é muito conveniente para deixar o componente com o direito de controlar a localização de vários elementos entre si, em vez de escolher um objeto filho.
Seção 4. Compreensão avançada setState
Aqui estão três perguntas fortemente relacionadas:
Pergunta 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'}
Questão 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'}
Pergunta 3
2 setState componentDidUpdate updating lifecycle 1) 1 2) 2 3) 3 4)
Análise das questões 1-3Todo o trabalho de setState é totalmente descrito aqui:
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
O fato é que setState não acontece de forma síncrona.
E caso existam várias chamadas para setState em uma linha, dependendo de estarmos dentro do método react-lifecycle, da função manipuladora do evento react (onChange, onClick) ou não, a execução de setState depende.
Dentro dos manipuladores de reação, o setState funciona em lotes (as alterações são roladas somente após o término das funções definidas pelo usuário na pilha de chamadas e entramos nas funções que chamamos nossos métodos de manipulador de eventos e ciclo de vida). Eles rolam um após o outro; portanto, se estamos dentro do manipulador de reação, obtemos:
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 as mudanças ocorreram batchevo.
Mas, ao mesmo tempo, se setState foi chamado fora dos react-handlers:
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 nesse caso, as alterações serão roladas separadamente.
Seção 5. Redux
Pergunta 1.
action, () => {} ? 1) . action type 2) , action type 3) , middleware action 4) , dispatch
Resposta e análise3) Sim, você precisa definir o middleware personalizado para essa ação
Tome redux-thunk como o exemplo mais simples. Todo o middleware é um pequeno bloco 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); };
Como o middleware funciona?
Eles assumem o controle antes que a ação chegue à loja. Portanto, uma ação que foi substituída primeiro passará pela cadeia de middleware.
Cada middleware aceita uma instância de armazenamento, o próximo método, que permite encaminhar a ação ainda mais, e a própria ação.
Se o middleware processar ações personalizadas, como redux-thunk, se ação for uma função, ela não encaminhará a ação mais, mas a afogará, chamando a ação com os métodos dispatch e getState passados para lá.
O que aconteceria se o redux-thunk fizesse a seguir para ação, que é uma função?
Antes de chamar redutores, a loja verifica o tipo de ação. Ele deve atender às seguintes condições:
1) Deve ser um objeto
2) Deve ter um campo de tipo
3) O campo de tipo deve ser do tipo string
Se uma das condições não for atendida, o redux lançará um erro.
Perguntas sobre bônus:
Pergunta bônus 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
Resposta e análisec) 2
getSnapshotBeforeUpdate
é uma função raramente usada no getSnapshotBeforeUpdate
que permite obter um instantâneo, que será passado para o componentDidUpdate. Esse método é necessário para pré-calcular determinados dados com base nos quais você pode fazer, por exemplo, uma solicitação de busca.
Pergunta de bônus 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)
Isso já é uma questão de conhecer novos recursos em reagir; não estava no nosso teste. Vamos tentar nos comentários para descrever em detalhes a operação do código da última pergunta :)