useState
使用状态
const [n,setN] = React.useState(0)
const [user,setUser] = React.useState({name:'Jack'})
注意事项
-
不可局部更新
如果
state
是一个对象,setState
不会自动合并属性import React, { useState } from "react"; import "./styles.css"; export default function App() { const [user,setUser] = useState({name:'Jack',age:18}) const Click = ()=>{ setUser({ name:'Tom' }) } return ( <div className="App"> <h1>{user.name}</h1> <h2>{user.age}</h2> <button onClick={Click}>Click</button> </div> ); }
效果
可以用
...
操作符解决 -
地址要改变
setState(obj)
如果obj
地址不变,React
就会认为数据没有变化import React, { useState } from "react"; import "./styles.css"; export default function App() { const [user,setUser] = useState({name:'Jack',age:18}) const Click = ()=>{ console.log('点击了') user.name = 'Tom' setUser(user) } return ( <div className="App"> <h1>{user.name}</h1> <h2>{user.age}</h2> <button onClick={Click}>Click</button> </div> ); }
效果
-
useState 接受函数
该函数返回初识
state
,且只执行一次,可以减少多余的计算过程const [user,setUser] = useState(()=>({name:'Jack',age:9+9}))
-
setN 接受函数
当执行下面的代码的时候,得出来的结果并不是如我们想的一样是2,而是1
import React, { useState } from "react"; export default function App() { const [n,setN] = useState(0) const Click = ()=>{ setN(n+1) setN(n+1) } return ( <div className="App"> <h1>{n}</h1> <button onClick={Click}>+1</button> </div> ); }
效果
用
setN
接受函数可以解决如上问题const Click = ()=>{ setN(i => i+1) setN(x => x+1) }
效果
useReducer
useReducer
是用来践行Flux/Redux思想
- 创建初始值
initialState
- 创建所有操作
reducer(state,action)
- 传给
useReducer
得到读和写API
- 调用
写({type:'操作类型'})
import React, { useReducer } from "react";
import "./styles.css";
const initial = {
n:0
}
const reducer=(state,action)=>{
if(action.type==='add'){
return {n:state.n+action.number}
}else if(action.type==='sub'){
return {n:state.n-action.number}
}else{
throw new Error("unkown type")
}
}
export default function App() {
const [state,dispatch]=useReducer(reducer,initial)
const {n}=state
const Click = ()=>{
dispatch({type:'add',number:2})
}
const Click2 = ()=>{
dispatch({type:'sub',number:1})
}
return (
<div className="App">
<h1>{n}</h1>
<button onClick={Click}>+2</button>
<button onClick={Click2}>-1</button>
</div>
);
}
效果
总的来说,useReducer
是 useState
的复杂版,注意useReducer
也不能自动合并属性
useContext
useContext
是局部的全局变量
- 使用
C =createContext(initial)
创建上下文 - 使用
<C.provider>
圈定上下文的作用域 - 在作用域内,使用
useContext(C)
来使用上下文
import React, { createContext, useState, useContext } from "react";
import "./styles.css";
const C = createContext(null)
export default function App() {
const [n,setN] = useState(0)
return (
<C.Provider value={{n,setN}}>
<div className="App">
<Father/>
</div>
</C.Provider>
);
}
function Father(){
return(
<div>
Father
<Son />
</div>
)
}
function Son(){
const {n,setN} = useContext(C)
const Click=()=>{
setN(n+1)
}
return(
<div>
<p>Son n:{n}</p>
<button onClick={Click}>+1</button>
</div>
)
}
效果
注意:
useContext
不是响应式的,在另一个模块改变C的值,另一个模块不会感知到这个变化,改变属性时,是通知App
重新渲染的
useEffect
函数组件中没有类组件中的生命周期,于是就有了 useEffect
API 的出现,利用该 API 模拟生命周期
模拟 constructor
函数组件执行的时候,就相当于 constructor
模拟 shouldComponentUpdate
用 React.memo 和 useMome 可以解决
模拟 render
函数组件的返回值就是 render 的返回值
模拟 componentDidMount
useEffect(()=>{
console.log('第一次渲染')
},[])
模拟 componentDidUpdate
useEffect(()=>{
console.log('任意属性变了')
})
useEffect(()=>{
console.log('属性n变了')
},[n])
模拟 componentWillUnmount
useEffect(()=>{
console.log('第一次渲染')
return ()=>{
console.log('组件要销毁了')
}
})
可以出现多个
useEffect
,会按照出现顺序依次执行
useLayoutEffect
useLayoutEffect
在浏览器渲染前执行
特点:
useLayoutEffect
总是比useEffect
先执行useLayoutEffect
最好影响了Layout
useMemo
useRef
绑定 DOM
import React, { useRef, useEffect } from "react";
import "./styles.css";
export default function App() {
const myRef = useRef(null)
useEffect(()=>{
myRef.current.value='输入框'
},[])
return (
<div className="App">
<input type="text" ref={myRef} />
</div>
);
}
效果
父组件调用子组件方法
import React, { useState, useRef, useImperativeHandle } from "react";
import "./styles.css";
export default function App() {
const myRef = useRef()
const Click =()=>{
myRef.current.add()
}
return (
<div className="App">
<div>Father</div>
<button onClick={Click}>+1</button>
<Son ref={myRef}/>
</div>
);
}
const Son = React.forwardRef((props,ref)=>{
const [n,setN]=useState(0)
useImperativeHandle(ref,()=>({
add:()=>{
setN(i=>i+1)
}
}))
return(
<div>
<div>Son: {n}</div>
</div>
)
})
效果
注意:
- 由于函数组件的
props
不包含ref
,需要使用React.forwardRef
,如上 useImperativeHandle
可以自定义ref
储存以前的值
import React, { useState, useEffect, useRef } from "react";
import "./styles.css";
export default function App() {
console.log('App执行了')
const [n,setN]=useState(0)
const count = useRef(0)
const Click =()=>{
setN(i=>i+1)
}
useEffect(()=>{
count.current+=1
console.log(count.current)
})
return (
<div className="App">
<div>{n}</div>
<button onClick={Click}>+1</button>
</div>
);
}
效果