useState
- 使用状态
const [n,setN]=React.useState(0)
const [user.setUser]=React.useState({name:'A'})
- 注意事项1:不可以局部更新
例:
如果我们只setUser的name时点击click会出现如下情况:
为了保证全部更新需要把之前的属性拷贝一份,做法如下:
import React, {useState} from "react";
import ReactDOM from "react-dom";
function App() {
const [user,setUser] = useState({name:'Dog', age: 3})
const onClick = ()=>{
setUser({
...user, // 这里实现拷贝之前的属性
name: 'Jack'
})
}
return (
<div className="App">
<h1>{user.name}</h1>
<h2>{user.age}</h2>
<button onClick={onClick}>Click</button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
- 注意事项2: 地址要变
setState(obj)如obj的地址不变,React就认为数据没有发生变化
- useState接受函数
const [state, setState] = useState(()=>{
return initialState
})
-
setState接受函数
setN(i => i + 1)}
useReducer
-
用来践行Flux/Redux的思想
分四步:
1,创建初始值initialState
2,创建所有操作
3传给useReducer,得到读和写API
4,调用写({type:操作类型})
import React, { useState, useReducer } from "react"; import ReactDOM from "react-dom"; //初始值 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); //得到都和写的API const { n } = state; const onClick = () => { dispatch({ type: "add", number: 1 }); //调用操作类型 }; const onClick2 = () => { dispatch({ type: "multi", number: 2 }); }; return ( <div className="App"> <h1>n: {n}</h1> <button onClick={onClick}>+1</button> <button onClick={onClick2}>+2</button> </div> ); } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);一个用useReducer的表单例子 链接
useContext
-
上下文
全局变量是全局的上下文
上下文是局部的全局变量
-
使用方法
1,使用C=createContext(initial)创建上下文
2,使用<C.provider>圈定作用域
3,在作用域内使用useContext(c)来使用上下文 代码链接
** 注意**:不是响应式的,在一个模块将C里面的值改变,另一个模块不会感知这个变化
import React, { createContext, useState, useContext } from "react";
import ReactDOM from "react-dom";
const C=createContext(null) //创建上下文
function App(){
const[n,setN]=useState(0)
return(
//圈定作用域
<C.Provider value={{n,setN}}>
{n}
<Parent/>
<Child/>
</C.Provider>
)
}
function Parent(){
return (
<div>
我是爸爸
</div>
)
}
function Child(){
// 3,在作用域内使用
const {n,setN}=useContext(C)
const onClick=()=>{
setN(i=>i+1)
}
return (
<div>
我是儿子
<button onClick={onClick}>+1</button>
</div>
)
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
useEffect
-
副作用(API名字叫的不大好)
1,对环境的改变即为副作用,如修改doucument.title,但不一定将副作用放在useEffect里,实际上叫afterRender更好,每次render后运行
-
用途:
1,作为componentDidMount使用,[]作为第二个参数
2,作为componentDidUpdate使用,可指定依赖
3,作为componentWillUnmount使用,通过return
-
特点
如果存在多个useEffect,会按照次序执行 效果链接
import React, { createContext, useState, useContext, useEffect } from "react";
import ReactDOM from "react-dom";
function App(){
const [n,setN]=useState(0)
const onClick=()=>{
setN(i=>i+1)
}
useEffect(()=>{
console.log('第一次渲染后执行')
},[]) // 里面的变量变化执行=>不会执行
useEffect(()=>{
if(n!==0){
console.log('n变化了')
}
},[n]) //n变化时执行
useEffect(()=>{
console.log('任何一个state变化都执行')
})
return (
<div>
n:{n}
<button onClick={onClick}>+1</button>
</div>
)
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
useLayoutEffect
-
布局副作用
useEffect在渲染完成后执行,useLayoutEffect在浏览器渲染前执行。
-
特点
1,useLayoutEffect总是比useEffect先执行
-
经验
1,为了用户体验优先使用useEffect(有限渲染)
useRef
- 目的:设定一个值在组件不断render时保持不变,
- 初始化:const count=useRef(0)
- 读取:count.current
- current是为了保证两次useRef是同一个值
forwardRef
- props无法传递ref属性情况
import React, { useRef } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
const buttonRef = useRef(null);
return (
<div className="App">
<Button2 ref={buttonRef}>按钮</Button2>
{/* 看浏览器控制台的报错 */}
</div>
);
}
const Button2 = props => {
return <button className="red" {...props} />;
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
- 实现ref传递
import React, { useRef } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
const buttonRef = useRef(null);
return (
<div className="App">
<Button3 ref={buttonRef}>按钮</Button3>
</div>
);
}
const Button3 = React.forwardRef((props, ref) => {
return <button className="red" ref={ref} {...props} />;
});
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);