创建一个函数组件
const Hello = (props) => {
return <div>{props.message}</div>
}
const Hello = props => <div>{props.message}</div>
function Hello(props){
return <div>{props.message}</div>
}
Hooks
useState
setN一定会触发重新渲染- 数据会被存入新的n
function App() {
const [n, setN] = React.useState(0);
return (
<div className="App">
<p>{n}</p>
<p>
<button onClick={() => setN(n + 1)}>+1</button>
</p>
</div>
);
}
function App() {
const [n, setN] = useState(0)
const onClick = ()=>{
setN(n+1)
setN(n+1) // 你会发现 n 不能加 2
// setN(i=>i+1)
// setN(i=>i+1) n可以+2
}
return (
<div className="App">
<h1>n: {n}</h1>
<button onClick={onClick}>+2</button>
</div>
);
}
useReducer
Flux/Redux思想
useState的复杂版,常用于表单
- 创建初始值
- 创建操作合集reducer(state,action)
- 传给useReducer,得到读写API
- 调用写API,传一个type ({type:'操作类型'})
代替Redux
- 将数据集中在一个store对象
- 将操作集中在reducer
- 创建一个Context
- 创建对数据的读写api
- 将读写api放入Context
- 用Context.Provider把api提供给所有组件
- 各个组件用useContext获取读写api
const initial = {
n: 0
};
const reducer = (state, action) => {
if (action.type === "add") {
return { n: state.n + action.number };
} else if (action.type === "multi") {
return { n: state.n * 2 };
} else {
throw new Error("unknown type");
}
};
function App() {
const [state, dispatch] = useReducer(reducer, initial);
const { n } = state;
const onClick = () => {
dispatch({ type: "add", number: 1 });
};
const onClick2 = () => {
dispatch({ type: "add", number: 2 });
};
return (
<div className="App">
<h1>n: {n}</h1>
<button onClick={onClick}>+1</button>
<button onClick={onClick2}>+2</button>
</div>
);
}
useContext
上下文,相当于在某一范围中充当「全局变量」的作用
从上至下逐级更新的过程,不是响应式过程(监听数据变化并做出反应)
- 创建一个Context
const C = createContext(null)
- 用C.Provider圈定范围
<C.Provider value={}>
const [ n, setN ] = useState(0)
<ComponentA />
<ComponentB />
</C.Provider>
- 在组件中使用
const { n, setN } = useContext(C)
useEffect
每次render后调用的函数
在函数组件中,作为componentDidMount, componentDidUpdate, componentWillUnmount使用
多个useEffect会按写代码时的先后顺序执行
useEffect(() => {}) //每次render后执行
useEffect(() => {}, []) //第一次渲染后执行
useEffect(() => {}, [n]) //n变化后执行
//如果数组中包含了所有的变量,那与第一种写法等价
useEffect(() => { return () => {}}) //return的函数会在该组件被噶了之前执行
useLayoutEffect
在浏览器改变外观前执行,区别于useEffect在浏览器改变外观之后
因此useLayoutEffect总是比useEffect先执行
useMemo
React.memo(ComponentA): ComponentA只在props更新后执行
const fn = useMemo(() => {
return () => {}
}, [m,n])
缓存内容,在页面刷新时缓存上一次的值,并使新渲染的组件使用该缓存值,新fn和旧fn为相同的两个空函数。只有m或者n变化时,fn才会重新得到新值
如果不使用useMemo,两次得到的fn会因为函数地址不同而被认为是两个不同的函数,从而触发组件的渲染,增加了渲染量,降低了性能
useCallback
useMemo简化版
const fn = useCallback(() => {}, [m,n])
useRef
让一个值在组件不断render时保持不变
const count = useRef(0)
useEffect(()=>{
count.current += 1
})
此时count记录了组件渲染的次数
使用.current的原因是:使用引用,来确保每次的渲染得到的新count都是指向同一个对象的引用
[scode type="yellow" size=""]count.current的变化不会触发UI的更新,需要调用useState来手动更新[/scode]
const [随便取一个,set随便取一个] = useState(null)
const onClick = () => {
count.current += 1
set随便取一个(Math.random())
}
//这样才会触发UI更新
forwardRef
React的函数组件需要使用forwardRef来获取外部传来的ref
function App() {
const buttonRef = useRef(null)
return(
<div>
<Button2 ref={buttonRef}>button</Button2>
</div>
)
}
const Button2 = React.forwardRef((props,ref) => {
return (<button ref={ref} {...props} />)
}
useImperativeHandle
自定义ref