Hey. September 24–25, HolyJs frontend developers conference https://holyjs-moscow.ru/ took place in Moscow. We came to the conference with our booth where we spent quiz. There was the main quiz - 4 qualifying rounds and 1 final, in which Apple Watch and Lego designers were raffled. And separately, we held a quiz on knowledge.
Under the cut - analysis of tasks quiz on react. The correct options will be hidden under the spoiler, so you can not only read the analysis, but also check yourself :)
Go!
For convenience, we have grouped the questions into sections:
Question 1.
react-: 1) SetProps, SetState, ForceUpdate 2) ForceUpdate, SetState 3) ForceUpdate, SetState, Parent (re)render 4) ForceUpdate, SetState, directly call UpdateComponent
3) ForceUpdate, SetState, Parent (re) render
Question 2.
, this.setState({}) react 1) , updating lifecycle 2) , 3) React "Object cannot be empty" 4) state
1) The component is marked dirty, updating lifecycle will be called
To answer the question we will analyze 2 parts:
1) Own component request for updating cycle
2) Query outside the component
The component itself has 2 ways to update itself:
1) this.setState and this.forceUpdate. In this case, the component will be marked dirty and on the Reconcilliation tick, if it is in priority for rendering, the updating cycle will start.
Interesting fact: this.setState({})
and this.forceUpdate
are different. When this.setState({})
is called, the full updating cycle is called, unlike this.forceUpdate
, when the updating cycle starts without the shouldComponentUpdate method. An example of how this.setState({})
works can be found here: https://codesandbox.io/s/m5jz2701l9 (if you replace forceS in the example setState with forceUpdate, you can see how the behavior of the components will change).
2) When the parent of the component is rendered, it returns the part of the vDOM, all the children that will have to be updated - and they will also have a full updating lifecycle. A full translation of the subtree can be avoided by describing shouldComponentUpdate or by defining the component as PureComponent.
Question 3
Component PureComponent (PC) 1) Component , Pure 2) PC SCU, shallowEqual props state 3) PC , store 4) PC shouldComponentUpdate
2) PC implements SCU, conducts shallowEqual props and state
As we discussed earlier, when (re) rendering the parent, the entire subtree will be sent to the updating lifeCycle. Imagine that you have updated the root element. In this case, the chain effect you should be updated almost the entire react-tree. In order to optimize and not send too much updating, there is a method shouldComponentUpdate
, which allows to return true if the component should be updated, and false otherwise. To simplify the comparison in react, you can inherit from PureComponent
to get immediately ready shouldComponentUpdate
, which compares by reference (if we are talking about object types) or by value (if we are talking about value types) all props and state that come into the component.
Question 4.
this.setState(() => {}, () => {}) — setState? 1) set . updating 2) state 3) setState 1
2) The second function will be called after the state is updated.
There are two methods in React-lifecycle: componentDidMount
for the mounting loop and componentDidUpdate for updating, where you can add some logic after updating the component. For example, make an http request, make some style changes, get the metrics of html elements and (by condition) make setState. If you want to do some kind of action after changing certain fields in the state, then in the componentDidUpdate
method you will have to write or compare:
componentDidUpdate(prevProp, prevState) { if (prevState.foo !== this.state.foo) { // do awesome things here } }
Or you can do this by setState:
setState( // set new foo {foo: 'baz'}, () => { // do awesome things here } );
Each approach has pros and cons (for example, if you change setState in several places, it may be more convenient to write a condition once).
Question 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
2) 2
Question 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
eleven
Question 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
eleven
Questions 5–7 Needed for the same thing - to check the understanding of the operation of PureComponent
and component updates during the transfer of props. If inside the render method we pass in the form of a jsx callback, describing it directly in the render function:
render () { return <Button onClick={() => {}} />; }
Then each render parent will update this click handler. This happens because with each render a new function is created with a unique link, which, when compared in PureComponent, will show that the new props are not equal to the old ones and the component needs to be updated. In the case when all checks pass and shouldComponentUpdate returns false, the update does not occur.
A detailed analysis of the work of the keys we published here: https://habr.com/company/hh/blog/352150/
Question 1.
key, ? 1) key 2) updating lifecycle 3) key 4) reconciliation
1) Delete the previous instance and make a new one when changing the key
Without using key reacts, it will compare the list of elements in pairs from top to bottom. If we use key, the comparison will occur on the corresponding key. If a new key appears, then such a component will not be compared with anyone and it will immediately be created from scratch.
This method can be used even if we have 1 element: we can set <A key="1" />
, in the next render we specify <A key="2" />
and in this case, react will delete <A key="1" />
and create from scratch <A key="2" />
.
Question 2.
this.prop.key? 1) 2) 3) static getKey
2) No
A component can learn the key from its children, which were given to it as a prop, but cannot find out about its key.
Question 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
2) 2
When the key is changed, the component will be recreated, so the render will be displayed twice.
Question 1.
. 1) prop / context 2) 3) setParentProps 4) static getParentRef
1) Callback in the form of prop / context
2) Take out the model layer and work through it
There are two correct answers here. The choice of any of them on the quiz will count you points. This question is for knowledge of data-flow react. Data from top to bottom is distributed in the form of props or context, there may be a callback, which the component below can call to affect the state of the system.
Another way of combining the model take-off, context and prop, is, for example, react-redux binding.
This library takes the model derived from the react (redux). Setit redux.store in Provider, which actually sets store in context. The developer then uses HOC connect, which goes into context, subscribes to the store (store.subscribe) changes, and when the store changes, it recalculates the mapStateToProps
function. If the data has changed, set it to props in the object being wrapped.
At the same time, connect allows you to specify mapDispatchToProps
, where the developer specifies the actionCreators to be transferred to the component. We, in turn, receive them from the outside (without context), bind the actionCreators
on the store (we wrap them in store.dispatch) and pass props to the wrapped component as props.
Question 2.
props jsx? 1) 2) children
1) In any
You can transfer to any. For example:
<Button icon={<Icon kind='warning'/>}></Button>
Draws a button with an icon. This approach is very convenient to use to leave the component the right to control the location of the various elements relative to each other, and not to touch one children prop.
Here are 3 strongly related questions:
Question 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'}
3) Not enough data
Question 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'}
2) {a: 'b1'}
Question 3.
2 setState componentDidUpdate updating lifecycle 1) 1 2) 2 3) 3 4)
eleven
All setState work is fully described here:
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
The fact is that setState does not occur synchronously.
And if there are several calls to setState in a row, then depending on whether we are inside the react-lifecycle method, the react-event handler function (onChange, onClick) or not, the execution of setState depends.
Inside the react handlers, setState runs batch (changes are rolled only after the user functions in the call stack have run out and we’ll fall into the functions that our event handler and lifecycle methods called). They roll in a row one after another, so in case we are inside a react-handler, we get:
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'
since the changes occurred batchevo.
But at the same time, if setState was called outside of the 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' +
As in this case, the changes will roll separately.
Question 1.
action, () => {} ? 1) . action type 2) , action type 3) , middleware action 4) , dispatch
3) Yes, you need to define custom middleware for such an action
Take redux-thunk as the simplest example. All middleware is a small block of code:
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); };
How do middleware work?
They get control before action comes to the store. Therefore, the action, which was zadispachen, first pass through a chain of middleware.
Each middleware accepts an instans store, the next method, which allows forwarding the action further, and the action itself.
If the middleware handles custom action, such as redux-thunk, for example, if it is an action, it does not forward the action, but “drowns out” it, instead calling the action and passing the dispatch method and getState to it.
What would happen if redux-thunk did next for action, which is a function?
Before calling the store, the store checks the action type. It must satisfy the following conditions:
1) It must be an object.
2) It should have a type field
3) The type field must be of type string
If one of the conditions is not met, redux will generate an error.
Bonus Question 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
c) 2
getSnapshotBeforeUpdate
is a rarely used function in react that allows you to get snapshots, which will then be passed to componentDidUpdate. This method is needed to calculate in advance certain data, on the basis of which you can then make, for example, a fetch query.
Bonus Question 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)
c) "Hello Hello World!"
This is a question about the knowledge of new features in the react, it was not in our quiz. Let's try in the comments to describe in detail the work of the code from the last question :)
Source: https://habr.com/ru/post/431492/
All Articles