不知不觉已经有一个多月时间没好好写过代码了,最近忽然发现react有关的东西有点忘了,所以赶紧趁着周末来恢复一下。
观前须知:并没有太难的东西,比较适合新手和很久没接触react的同学。因为本篇更多的是我对于一些react相关题的分享,所以并没有太多的解析,更多的关注点是题目本身。
认知训练-渲染篇
恢复一下渲染相关的知识点,这篇题目比较基础,因此我就直接在代码最下方贴出答案。
Memo & re-render
下面代码的输出是啥?
import React, { useState, useEffect, memo } from 'react'
import ReactDOM from 'react-dom'
function A() {
console.log('A')
return <B/>
}
const B = memo(() => {
console.log('B')
return <C/>
})
function C() {
console.log('C')
return null
}
function D() {
console.log('D')
return null
}
function App() {
const [state, setState] = useState(0)
useEffect(() => {
setState(state => state + 1)
}, [])
console.log('App')
return (
<div>
<A state={state}/>
<D/>
</div>
)
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App/>)
// "App" "A" "B" "C" "D" "App" "A" "D"
Context & re-render 1
import React, { useState, memo, createContext, useEffect, useContext} from 'react'
import ReactDOM from 'react-dom'
const MyContext = createContext(0);
function B() {
const count = useContext(MyContext)
console.log('B')
return null
}
const A = memo(() => {
console.log('A')
return <B/>
})
function C() {
console.log('C')
return null
}
function App() {
const [state, setState] = useState(0)
useEffect(() => {
setState(state => state + 1)
}, [])
console.log('App')
return <MyContext.Provider value={state}>
<A/>
<C/>
</MyContext.Provider>
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App/>)
// "App" "A" "B" "C" "App" "B" "C"
Context & re-render 2
import React, { useState, createContext, useEffect, useContext} from 'react'
import ReactDOM from 'react-dom'
const MyContext = createContext(0);
function B({children}) {
const count = useContext(MyContext)
console.log('B')
return children
}
const A = ({children}) => {
const [state, setState] = useState(0)
console.log('A')
useEffect(() => {
setState(state => state + 1)
}, [])
return <MyContext.Provider value={state}>
{children}
</MyContext.Provider>
}
function C() {
console.log('C')
return null
}
function D() {
console.log('D')
return null
}
function App() {
console.log('App')
return <A><B><C/></B><D/></A>
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App/>)
//App A B C D A B
认知训练-hooks篇
useEffect
import React, { useEffect, useState } from 'react'
import ReactDOM from 'react-dom'
function App() {
const [state, setState] = useState(0)
console.log(state)
useEffect(() => {
setState(state => state + 1)
}, [])
useEffect(() => {
console.log(state)
setTimeout(() => {
console.log(state)
}, 100)
}, [])
return null
}
ReactDOM.render(<App/>, document.getElementById('root'))
先说答案:0 0 1 0
解析:这里主要涉及到一个概念叫 "capture value",简单来说就是react每次渲染都是一个独立的过程,其中的绝大多数数据(像ref就是例外)它都不是你认为都“最新的值”,或者说是“不变“的。关于captur value可以看这里:juejin.cn/post/707941…
useRef
import React, { useRef, useEffect, useState } from 'react'
import ReactDOM from 'react-dom'
function App() {
const ref = useRef(null)
const [state, setState] = useState(1)
useEffect(() => {
setState(2)
}, [])
console.log(ref.current?.textContent)
return <div>
<div ref={state === 1 ? ref : null}>1</div>
<div ref={state === 2 ? ref : null}>2</div>
</div>
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App/>)
答案:undefined 1
解析:useRef的简单应用。初始化渲染时为1,由于ref保持同一引用,第二次渲染时为1.
实践训练-hooks篇
来实现一个自定义hooks,useFocus。
先看用法:
function App() {
//当目标input被选中时,isFocused为true。
const [iRef, isFocused] = useFocus()
return
<div>
<input ref={iRef}/>
{isFocused && <p>focused</p>}
</div>
}
小提示:注意iRef动态绑定的时的处理。
测试用例:先给大家一个测试用例
export function App() {
const [ref, isFocused] = useFocus<HTMLInputElement>();
const [targetRef, setTargetRef] = React.useState(0);
return <div>
<button onClick={() => { setTargetRef((pre) => (pre + 1) % 2) }}>change current:{targetRef}</button>
<input ref={targetRef == 0 ? ref : null} />
<input ref={targetRef == 1 ? ref : null} />
{isFocused && <p>focused</p>}
</div>
}
思路:思路其实很简单,可以返回一个ref,且监听ref.current的"focus"事件即可。
import React, { Ref } from 'react'
export function useFocus<T extends HTMLElement>(): [Ref<T>, boolean] {
const ref = React.createRef<T>();
var [isFocused, setIsFocused] = React.useState(false);
React.useEffect(() => {
function onFocus() {
setIsFocused(true);
}
function onBlur() {
setIsFocused(false);
}
const el = ref.current;
//小问题1: 这里不用el可以吗? 下文el改成ref.current
if (el) {
el.addEventListener('focus', onFocus)
el.addEventListener('blur', onBlur)
}
return () => {
if (el) {
el.removeEventListener('focus', onFocus);
el.removeEventListener('blur', onBlur);
}
}
})
return [ref, isFocused]
}
小问题1解答: 不可以,如果把el改为ref.current,那么对于我们的测试用例来说,它在执行销毁函数(line:18)时,ref已经被修改,此时ref.current获取到的是最新的dom节点。
拓展:
//该种写法摘自评论区
import React, { Ref, useRef, useCallback, useState } from 'react'
export function useFocus<T extends HTMLElement>(): [Ref<T>, boolean] {
const [focused, setFocused] = useState(false)
const ref = useRef<T>()
const onFocus = () => {
setFocused(true)
}
const onBlur = () => {
setFocused(false)
}
const callbackRef = useCallback((node: T) => {
if (ref.current) {
ref.current.removeEventListener('focus', onFocus)
ref.current.removeEventListener('blur', onBlur)
}
ref.current = node
if(node) {
ref.current.addEventListener('focus', onFocus)
ref.current.addEventListener('blur', onBlur)
}
}, [])
return [callbackRef, focused]
}
更多
本篇所以题目均出自bigfrontend.dev/react,感兴趣的同…