Hooks 定义
Hook 就是钩子的意思。为了在函数组件中使用类组件的生命周期、状态管理等一些类组件特性,所以引入了 hooks.
需要注意的是:在类组件中是不能够使用hooks的。
Hooks 使用规则
- Hooks只能在顶层调用,不能在循环、条件判断或者嵌套函数中调用hook函数。
// 错误写法
if(a===b){
useEffect(()=>{
//...
},[...])
}
- 只能在函数组件中使用hooks,不能在类组件中使用
- 可以使用 eslint-plugin-react-hooks插件规范hooks代码
Hooks 优缺点
优点
- 代码容易复用(可以自定义useState)
- 代码可读性强。代码好看些,每个功能都在函数中代码量少
- 组件层级变少
- 不需要考虑this 的指向问题
缺点
- 响应式的 useEflect , useCalbask 的作用、第二个参数(依赖项数组)的改变时机不容易把握。
- 状态不同步,不在同一个 ui 线程,同一个变量,会显示不同的值。可以使用 useRef 改善。
- 状态管理容易出错
- 需要了解所有 hooks 函数
函数列表
useState
- 用来声明状态变量,类似类组件class 中的this.setState({a:'abc'})。而且函数的渲染每次都是独立的(异步),这也就是 Capture Value 特性
- 示例
// 定义
const [nameState, setName] = useState("");// useState 的参数为设定的默认值
const [age, setAge] = useState(1);
// 使用
setName("ABC");
setAge(25);
- 坑
- 点击按钮改变nameState值,当回车 Enter(或者不在同一个线程中,初始时注册的方法等)提交时,获取 nameState 值,这时发现nameState还是默认的初始值。例如:点击按钮提交和按回车键提交,点击按钮提交是可以获取到值的,按回车键提交获取值失败。
- 解决方法:
- 方法一
const [nameState,setName]=useState();
const lastNameState = useRef(nameState);
// 改变 lastNameState 值
useEffect(()=>{
lastNameState.current= nameState;
}[nameState]);
// 点击按钮时,调用setName
const onClick=(e)=>{
setName(e.currentTarget.value)
}
- 方法二
useEffect(()=>{
setName(name)
},[name]);
//点击按钮时,发送一个action,改变reducer 的state值
const onClick=(e)=>{
putName(e.currentTarget.value)
}
//...
// Action
export function putName(name){
return{
type:"CHANGE_NAME",
payload: { name }
}
}
useEffect
- 网上都叫其副作用,我也不明白为啥都叫副作用 ♂️,我更倾向于叫响应。
- 其替代类组件声明周期函数: componentDidMount、componentWillUnmount、componentDidUpdate。
- 示例
// componentDidMount || componentWillUnmount
useEffect(()=>{
// componentDidMount
// ...
// componentWillUnmount
return ()=>{
//...
},[]);
// componentDidMount || componentDidUpdate
useEffect(()=>
setName(name)
),[name]);// 从父组件传递过来的name,当name 发生变化时,更新nameState
useContext
- 用来处理多层级传递数据,减少组件的嵌套。
- 示例
function Children(){
const color = useContext(colorContext);
return <div>{color}</div>
}
function Parent(){
return <Children/>
}
const colorContext = createContext("gray");// React.createContext();
function App(){
return (
<colorContext.Provider value={"red"}>
<Parent/>
</colorContext.Provider>
)
}
useReducer
- 可以看成是react-redux 使用方式的缩小版。不能使用redux 提供的中间件。
- 可以看成是useState 的替代方案
- useReducer(reducer, initialState, init())。reducer函数,接收state和action; initialState是需要设定的state的初始值;init()是通过方法来初始化state,这样可以情性的创建初始state。
- 示例
const initialState =0;
const init = (v) =>{
return v;
}
const testReducer =(state, action)=>{
switch(action){
case "add":
return state+1;
case"sub":
return state-1;
case "reset":
return init(action.payload);
default:
return state;
}
}
const[count, calculationDispatch]= useReducer(testReducer, initialState);
// const[count, calculationDispatch] = useReducer(testReducer, initialState, init);
return(
<div>
<div>{count}</div>
<button onClick={()=>calculationDispatch("add")}>ADD</button>
<button oClick={()=>calculationDispatch("sub")}>SUB</button>
<button oClick={()=>calculationDispatch({type: "reset",payload:initialState})}>RESET</button>
</div>
)
useCallback
- 获得一个记忆函数,避免在某些情况下子组件的重复渲染。一般用来做性能优化。
- useCallback 返回的是一个函数
- useCallback(function,[...])。两个参数,第一个参数是需要记忆的画数,第二个参数是当数组内的值发生变化了,才执行function。
- 示例
const [text, setText] = useState("");
const submit = useCallback(()=>{
console.log(text)
}, [text]);
return(
<div>
<input value={text} onChange={(e)=>setText(e.target.value)}/>
<button onClick={submit}>Submit</button>
</div>
)
useMemo
- 获得一个记忆组件,适用于返回确定的值
- 类似useCallback,但是useCallback返回的是需要执行的函数,而useMemo 返回的是执行函数返回的值
- 示例
const [text, setText] = useState("");
const textMemo = useMemo(()=>{
return text+"time"
}, [text]);
return(
<div>
<input value= {text} onChange={(e)=>setText(e.target.value)}/>
<input value={textMemo}/>
</div>
)
useRef
- 生成对DOM对象的引用。
- 类似于 createRef,但是它是真正的引用,而不是把值copy 过去。
- 与useState很像,可以把它看成是render的全局变量,而useState是每个render中局部变量; useRef.current 不会触发Ul的重新渲染,而useState会触发Ul的重新渲染。
- 示例
const [name, setName] = useState("");
const nameRef = useRef(name);
const submit =() =>{
setName(nameRef.current);
}
return(
<div>
<p>{name}</p>
<div>
<input ref ={nameRef}/>
<button onClick={submit}>Submit</button>
</div>
</div>
)
useImperativeHandle
- 穿透Ref,用于父组件获取子组件的引用
- 示例
function ChildInputComponent(props, ref){
const inputRef = useRef(null);
useImperativeHandle(re, ()=>inputRef.current);
return(
<div>
<input name="input1" ref={inputRef} placeholder="yes"/>
<input name="input1" placeholder="nonono"/>
</div>
)
}
const ChildInput = forwardRef(ChildInputComponent);
function App(){
const inputRef = useRef(null);
useEffect(()=>{
inputRef.current.focus()
},[]);
return(
<div>
<ChildInput ref={inputRef}/>
</div>
)
}
useLayoutEffect
- 同步执行,在页面完全渲染完成后,操作 dom, 会优于 useEffect 异步触发函数。
其他
memo
- 用来包裹函数式组件的
- 当用React.memo包裹组件时,当组件内的useState或useContext 发生变化时,才会重新渲染面面。
- memo(Component, propsAreEqual(preProps, nextProps));第一个参数为函数组件,第二个参数为比较规则,若不传递则默认为props 浅比较。
forwardRef
- 主要用来进行DOM 穿透的
- 示例
const AButton = React.forwardRef((props, ref)=>(
<button ref={ref} {...props}>
{props.children}</button>
))
class BButton extends React.Component{
this.testRef = React.createRef();
onClick =()=>{
// 打印出 AButton 中button 的信息
console.log(this.testRef.current)
};
render(){
return(
<div>
<AButton ref = {this.testRef} onClick={onClick}>AButtan</AButton>
</div>
)
}
}
总结
这里只罗列了hooks 函数的基本用法,想要更深层次的掌握react 的知识,需要不断的去实践。只有在不断的使用各种技巧、各种功能,才会越发的了解掌握更深入的内容。
网友评论