npx create-react-app my-app
以 use 开头的函数,只能在组件或自定义 Hook 的最顶层调用,不能在条件语句、循环语句或其他嵌套函数内调用 Hook。
在组件中添加状态变量
const [state, setState] = useState(initialState);
侦听状态变化,并执行相应逻辑
useEffect(setup, dependencies?)
不带参数的 useEffect 函数中不能使用useState方法来改变state,否则会触发无限循环
//不带参数,每次渲染后执行
useEffect(() => {
console.log("count", count);
});
当 useEffect 带参数时,参数的变化才会触发 useEffect 的执行,不变化不执行。可使用 useState
useEffect(() => {
console.log("count", count);
console.log("num", num);
}, [num]);
当 useEffect 传入空参数 [] 时,只有当第一次渲染(mount) 和不渲染(unmount)时执行。
useEffect(() => {
console.log("count", count);
}, []);
父子组件传递状态数据。createContext 能够创建组件间共享的上下文状态,然后通过 useContext 在组件中使用这些状态
import { createContext, useContext } from 'react';
const ThemeContext = createContext(null);
export default function MyApp() {
return (
<ThemeContext.Provider value="dark">
<Button>显示的主题</Button>
</ThemeContext.Provider>
)
}
function Button({ children }) {
const theme = useContext(ThemeContext);
const className = 'button-' + theme;
return (
<button className={className}>
{children}
</button>
);
}
useReducer 是 useState 的替代方案。当你想更新的状态中包含多个子值时 或者 当你想更新一个状态,并且这个状态更新依赖于另一个状态的值时,可以使用useReducer,reducer 可以让你把组件内发生了什么(actions)和状态如何响应并更新分开表述。
结合useContext 可以实现 Redux 组件间共享状态管理
const [state, dispatch] = useReducer(reducer, initialArg, init?)
第三个参数如果没值,则将初始状态设置为initialArg。否则,将初始状态设置为呼叫init(initialArg)的结果
import { useReducer } from 'react';
function reducer(state, action) {
switch (action.type) {
case "minus_age":
return {
age: state.age > 0 ? (state.age - 1) : 0
};
default:
return {
age: state.age + 1
};
}
}
export default function Counter() {
const [state, dispatch] = useReducer(reducer, { age: 42 });
return (
<>
<button onClick={() => {
dispatch({ type: 'add_age' })
}}>
Add age
</button>
<button onClick={() => {
dispatch({ type: 'minus_age' })
}}>
Minus age
</button>
<p>Hello! You are {state.age}.</p>
</>
);
}
和useState的setState不一样的是,useReducer返回的dispatch函数是用来触发某些改变state的action而不是直接设置state的值
相当于计算属性
const cachedValue = useMemo(calculateValue, dependencies)
export default function Counter() {
const clickFn = useMemo(() => {
let sum = 0;
for (let i = 0; i < 100*count; i++) {
sum += i;
}
return sum;
},[count]);
//当依赖的count发生改变才去重新计算,return值出来
return(
<div>获得总数{clickFn}</div>
)
}
避免子组件不必要的重新渲染,依赖数据改变传入新函数否则保留原函数
const cachedFn = useCallback(fn, dependencies)
useMemo和useCallback接收的参数都是一样,都是在其依赖项发生变化后才执行,都是返回缓存的值。而useEffect没有返回
区别在于useMemo返回的是函数运行的结果,useCallback返回的是函数。
useCallback(fn, deps) 与 useMemo(() => fn, deps). 是等价的,
useRef 返回一个引用了DOM的对象,返回的对象将在组件的整个生存期内持续存在。相当于为this赋值
const ref = useRef(initialValue)
import { useRef } from 'react';
export default function Form() {
const inputRef = useRef(null);
function handleClick() {
//点击使input框获取焦点
inputRef.current.focus();
}
return (
<>
<input ref={inputRef} />
<button onClick={handleClick}>
Focus the input
</button>
</>
);
}
useRef和createRef的区别是createRef 每次渲染都会返回一个新的引用,而 useRef 每次都会返回相同的引用,可以在 useRef 对象上存放缓存的值。
转发ref,将dom暴露给父组件
import { forwardRef, useRef } from 'react';
const MyInput = forwardRef(function MyInput(props, ref) {
return (
<label>
{props.label}
<input ref={ref} />
</label>
);
});
export default function Form() {
const ref = useRef(null);
function handleClick() {
ref.current.focus();
}
return (
<form>
<MyInput label="Enter your name:" ref={ref} />
<button type="button" onClick={handleClick}>
Edit
</button>
</form>
);
}
使用forwardRef和useImperativeHandle限制父组件调用子组件的Api
import { forwardRef, useRef, useImperativeHandle } from 'react';
const MyInput = forwardRef(function MyInput(props, ref) {
const inputRef = useRef(null);
useImperativeHandle(ref, () => {
return {
focus() {
inputRef.current.focus();
},
scrollIntoView() {
inputRef.current.scrollIntoView();
},
};
}, []);
return <input {...props} ref={inputRef} />
});
export default function Form() {
const ref = useRef(null);
function handleClick() {
//父组件获得了 MyInput 的 ref,能通过该 ref 来调用 focus 和 scrollIntoView 方法
//但是它的访问是受限的,无法读取或调用下方 DOM 节点的其他所有属性和方法
ref.current.focus();
// 下方代码不起作用,因为 DOM 节点并未被暴露出来:
// ref.current.style.opacity = 0.5;
}
return (
<form>
<MyInput label="Enter your name:" ref={ref} />
<button type="button" onClick={handleClick}>
Edit
</button>
</form>
);
}
ref有一个current属性,可以对这个属性进行操作,用于获取DOM元素和保存变化的值。
不要滥用ref,仅在没法通过 prop 来表达 命令式 行为的时候才使用 ref,比如滚动到指定节点、聚焦某个节点、触发一次动画、选择文本等
如果可以通过prop实现,就不要使用ref ,useEffect 可以通过 prop 来暴露一些命令式的行为