你好 9月24日至25日,HolyJs前端开发人员会议https://holyjs-moscow.ru/在莫斯科举行。 我们以自己的立场参加了大会,并进行了测验。 有一个主要测验-4轮资格赛和1轮决赛,参加了Apple Watch和乐高玩具制造商的比赛。 另外,我们进行了反应知识测验。
在猫之下-解析测验任务上的反应。 正确的选项将隐藏在扰流板下,因此您不仅可以阅读分析,还可以自己检查一下:)

走吧
为了方便起见,我们将这些问题分为几部分:
第1节。基本了解this.setState的操作和更新组件生命周期:
问题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(重新)渲染
问题2
, this.setState({}) react 1) , updating lifecycle 2) , 3) React "Object cannot be empty" 4) state
问题1和2的分析为了回答这个问题,我们将分析两个部分:
1)自有组件请求更新周期
2)在组件外请求
组件本身有两种更新方式:
1)this.setState和this.forceUpdate。 在这种情况下,该组件将被标记为肮脏,并且在重新协调上打勾,如果优先渲染,则将开始更新周期。
有趣的事实: this.setState({})
和this.forceUpdate
是不同的。 this.setState({})
将调用完整的更新循环,这与this.forceUpdate
不同,当更新循环开始时没有shouldComponentUpdate方法时。 可以在以下位置找到this.setState({})
的工作示例: https ://codesandbox.io/s/m5jz2701l9(如果在示例中将setState替换为forceUpdate,则可以看到组件的行为如何变化)。
2)重新渲染组件的父级时,它将返回vDOM部分,所有需要更新的子级,它们也称为完整更新生命周期。 通过描述shouldComponentUpdate或将组件定义为PureComponent可以避免完全重述子树。
问题3
Component PureComponent (PC) 1) Component , Pure 2) PC SCU, shallowEqual props state 3) PC , store 4) PC shouldComponentUpdate
回答和解析2)PC实施SCU,进行浅层平等的道具和状态
如前所述,当(重新)渲染父树时,整个子树将被发送到更新的生命周期。 想象一下,您已经更新了根元素。 在这种情况下,根据连锁效应,您将必须更新几乎整个反应树。 为了优化而不发送不必要的更新,在响应中有一个方法shouldComponentUpdate
,如果您应该更新组件,则该方法允许您返回true,否则返回false。 为了简化react中的比较,您可以从PureComponent
继承以立即准备shouldComponentUpdate
,它shouldComponentUpdate
引用(如果涉及对象类型)或按值(如果涉及值类型)比较组件中的所有属性和状态。
问题4。
this.setState(() => {}, () => {}) — setState? 1) set . updating 2) state 3) setState 1
回答和解析2)更新状态后将调用第二个函数
React生命周期中有两种方法:用于安装循环的componentDidMount和用于更新的componentDidUpdate,您可以在其中更新组件后添加一些逻辑。 例如,发出一个http请求,进行一些样式更改,获取html元素的度量,并(按条件)进行setState。 如果要更改状态中的某些字段后要采取某些措施,则必须在componentDidUpdate
方法中编写一个比较:
componentDidUpdate(prevProp, prevState) { if (prevState.foo !== this.state.foo) { // do awesome things here } }
或者您可以通过setState来做到这一点:
setState( // set new foo {foo: 'baz'}, () => { // do awesome things here } );
每种方法各有利弊(例如,如果您在多个位置更改setState,则一次编写条件可能更方便)。
问题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
问题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
问题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
问题5-7分析问题5–7对于同一件事也是必需的-检查对PureComponent
工作的PureComponent
以及在道具转移时对组件的更新。 如果在render方法内部,我们传入一个jsx回调,直接在render函数中对此进行描述:
render () { return <Button onClick={() => {}} />; }
然后,父项的每个渲染将更新给定的单击处理程序。 发生这种情况是因为每个渲染器都创建了一个具有唯一链接的新函数,与PureComponent进行比较时,该链接将显示新的道具与旧的道具不相等,您需要更新组件。 如果所有检查都通过,并且shouldComponentUpdate返回false,则不会发生更新。
第2章React中的键
我们在这里发布的密钥的详细分析: https : //habr.com/company/hh/blog/352150/
问题1
key, ? 1) key 2) updating lifecycle 3) key 4) reconciliation
回答和解析1)更改密钥时,删除前一个实例并挂载新实例
在不使用键的情况下,react将从顶部到底部成对比较元素列表。 如果使用键,则将在相应的键上进行比较。 如果出现了新的密钥,那么将不会与任何人比较这样的组件,而是会立即从头开始创建它。
即使我们有1个元素,您也可以使用此方法:我们可以设置<A key="1" />
,在下一个渲染中,我们指定<A key="2" />
,在这种情况下react将删除<A key="1" />
并从头开始创建<A key="2" />
。
问题2
this.prop.key? 1) 2) 3) static getKey
回答和解析2)没有
组件可以从其子级(作为道具传递给它的子项)中学习密钥,但是无法了解其密钥。
问题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
更改键时,将重新创建组件,因此渲染将显示两次。
第3章关于jsx的问题
问题1
. 1) prop / context 2) 3) setParentProps 4) static getParentRef
回答和解析1)以prop / context形式进行回调
2)删除模型层并进行遍历
有两个正确答案。 在测验中选择其中任何一个都可以算出您的分数。 这个问题是为了了解数据流的反应。 数据从上到下以props或context的形式分布,它可以包含一个回调,下面的组件可以调用该回调来影响系统的状态。
结合模型移除,上下文和属性的另一种方法是,例如,react-redux绑定。
该库采用从react(redux)派生的模型。 在Provider中设置redux.store,实际上在上下文中设置store。 然后,开发人员使用进入上下文的HOC连接,订阅存储(store.subscribe)更改,并且当存储更改时,重新计算mapStateToProps
函数。 如果数据已更改,则会将其设置为包装对象的props。
同时,connect允许您指定mapDispatchToProps
,开发人员在其中指定必须传递给组件的那些actionCreators。 反过来,我们从外部(没有上下文)接收它们,将actionCreators
绑定到商店(将它们包装在store.dispatch中),并将它们作为道具传递给包装的组件。
问题2
props jsx? 1) 2) children
回答和解析1)在任何
您可以转移到任何一个。 例如:
<Button icon={<Icon kind='warning'/>}></Button>
绘制带有图标的按钮。 这种方法非常方便使用,以使组件有权控制各种元素相对于彼此的位置,而不是挑选一个儿童道具。
第4章高级了解setState
以下是3个密切相关的问题:
问题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'}
问题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'}
问题3。
2 setState componentDidUpdate updating lifecycle 1) 1 2) 2 3) 3 4)
问题1-3的分析setState的所有工作在此处进行了完整描述:
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
事实是setState不会同步发生。
并且如果连续有多个调用setState的情况,则取决于我们是否在react-lifecycle方法内部,react-event的处理函数(onChange,onClick)内,取决于setState的执行。
在React处理程序内部,setState可以批量工作(只有在调用堆栈中的用户定义函数结束并且进入调用事件处理程序和生命周期方法的函数之后,更改才会滚动。) 他们一个接一个地滚动,所以如果我们在react-handler里面,我们得到:
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'
由于更改发生batchevo。
但是同时,如果setState在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' +
由于在这种情况下,更改将单独滚动。
第5节Redux
问题1
action, () => {} ? 1) . action type 2) , action type 3) , middleware action 4) , dispatch
回答和解析3)是,您需要为此操作定义自定义中间件
以redux-thunk作为最简单的示例。 所有中间件都是一小段代码:
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); };
中间件如何工作?
他们在动作到达商店之前就获得了控制权。 因此,已被覆盖的操作将首先通过中间件链。
每个中间件都接受一个存储实例,一个next方法(该方法允许您进一步转发操作)以及操作本身。
如果中间件处理自定义动作,例如redux-thunk,则如果动作是一个函数,则它不会进一步转发该动作,而是“淹没”该动作,而是通过传递给它的dispatch和getState方法调用该动作。
如果redux-thunk接下来执行该操作(该功能),会发生什么情况?
在调用reducers之前,store检查动作的类型。 它必须满足以下条件:
1)必须是一个对象
2)它应该有一个类型字段
3)类型字段必须为字符串类型
如果不满足其中一个条件,则redux将引发错误。
奖励问题:
奖金问题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
是getSnapshotBeforeUpdate
中很少使用的函数,它允许您获取快照,然后将快照传递给componentDidUpdate。 需要这种方法来预先计算某些数据,然后您可以基于这些数据发出例如提取请求。
奖金问题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)
这已经是知道反应中新功能的问题;这不在我们的测验中。 让我们尝试在注释中详细描述最后一个问题的代码操作:)