Hooks: useState状态管理
记 17 中记录了Hooks: useState状态管理的使用
Hooks: useContext状态传递
记 18 中记录了Hooks: useContext状态传递的使用
Hooks: useReducer同一状态管理
useReducer 和 useState 非常相似,可以更好的管理同一状态的多种复杂操作。而且 它可以让你把状态更新逻辑从事件处理函数中移动到组件外部。
import { useReducer } from 'react';
//第一个参数表示reducer当前要管理的状态,第二个参数表示要对当前状态做的操作
function countReducer(state, action) {
switch (action.type) {
case 'add':
return state + 1;
case 'minus':
return state - 1;
default:
return state;
}
}
export default function App() {
//返回数组的第一个参数表示状态初始值,第二个参数是用来进行状态修改的状态触发器
const [state, dispatch] = useReducer(countReducer, 0);
return (
<>
<button onClick={() => {
dispatch({ type: 'minus' })
}} > - </button>
<span>Hello! state {state}.</span>
<button onClick={() => {
dispatch({ type: 'add' })
}} > + </button>
</>
);
}
Hooks: useRef
记录并引用之前的状态值
import { useRef, useState } from 'react';
export default function App() {
//返回数组的第一个参数表示状态初始值,第二个参数是用来进行状态修改的状态触发器
const [count, setCount] = useState(0);
const prevCount = useRef();
// 设置preCount.current,本质是调用useRef返回的一个可变的ref对象,其.current属性被初始化为传入的参数(用来记住count的值)。它的值可以做修改,但并不是响应式的状态
function handleClick() {
prevCount.current = count;
setCount(count + 1)
}
return (
<>
<p>Hello! new count! {count}. </p>
<p>old count! {prevCount.current}. </p>
<button onClick={handleClick} >+1</button>
</>
);
}
记录并引用页面中的标签
export default function App() {
const inputRef = useRef(null);
function handleClick() {
inputRef.current.focus();
}
return (
<>
<input type="text" ref={inputRef}/>
<button onClick={handleClick} >按钮</button>
</>
);
}
记录并引用页面中其他组件
react中默认子组件是不对外开放的。因此我们不能直接使用ref来获取子组件的实例对象,如果我们想要访问其他组件内部的功能,需要进行以下设置
- 必须使用
forwardRef方法(高阶组件)定义函数 需要在子组件中使用 forwardRef(高阶组件)定义函数内容,它可以将子组件中绑定ref的DOM转发给父组件。 - 使用
useImperativeHandle方法暴露子组件内部的函数功能及其他属性方法
import { useImperativeHandle, forwardRef, useRef} from 'react';
//forwardRef接收一个函数作为参数,该函数接收两个参数,第一个参数是子组件的props,第二个参数是子组件中传递给父组件的绑定ref的DOM
const Child = forwardRef(function (props, ref) {
//useImperativeHandle接收两个参数,第一个参数是绑定ref的DOM,第二个参数是一个函数,该函数返回一个对象,该对象就是子组件暴露给父组件的属性方法,需要用小括号将对象包裹起来
useImperativeHandle(ref, () => ({
//暴露给父组件的属性方法
myFn: () => {
//子组件的方法
console.log('子组件的方法');
}
}))
return (
<div>
<p>我是子组件</p>
</div>
)
})
export default function App() {
const childRef = useRef();
function handleClick() {
childRef.current.myFn();
}
return (
<>
<Child ref={childRef} />
<button onClick={handleClick} >按钮</button>
</>
);
}
Hooks: useEffect
副作用函数。例如我们需要在组件加载、更新等非用户触发类事件上添加副作用事件(执行接口请求)时,我们就可以使用useEffect钩子
useEffect(() => {
// 组件挂载时执行
console.log('App 组件挂载');
return () => {
console.log('App 组件卸载');
}
//第二个参数传入依赖数组,只有当依赖数组中的值发生变化时才会执行,空数组表示组件挂载时执行一次,任何情况都不再执行
//如传入count,那么当count发生变化时就会执行
}, [count]);
Hooks: useMemo
useMemo是用来进行数据缓存的钩子。react中子组件会随父组件状态的变化而变化,当子组件中需要进行复杂计算时,如果父组件状态变更导致重新渲染,子组件也会重新执行计算操作,哪怕数据并没有发生变化。这时我们就可以使用useMemo将计算结果进行缓存
//父组件中设置几个状态项 如count和input 当我们不希望改变count状态值后子组件中input的计算属性重新渲染,就可以使用useMemo
//子组件中
function Computed({ value }) {
//每次操作后都会将计算结果缓存到result中
const result = useMemo(() => {
console.log('计算中...');
// 计算
return value * 2;
//第二个参数是依赖项数组,当数组中指定代码发生变化后,才会重新计算
}, [value]);
return (
<div>
<p>input: {value}</p>
<p>result: {result}</p>
</div>
)
}
Hooks: useCallBack
useCallBack用来缓存函数,同样的,当父组件渲染时子组件也同时被重新渲染,可以将子组件设置为记忆组件,
- 使用react的memo函数将组件变更为记忆组件
- 使用useCallback钩子来保证函数的不变性 通过这些步骤,当父组件变更时,传入子组件的函数也不会发生变化,子组件就不会重新渲染
import { memo, useMemo, useState } from 'react';
//设置子组件为记忆组件,这样当传入的props没有发生变化时,子组件就不会重新渲染
const Button = memo(function Button({ onClick }) {
console.log('button render')
return (
<div>
<button onClick={onClick}>子组件</button>
</div>
)
})
export default function App() {
const [count, setCount] = useState(0);
//父组件渲染后,组件中的函数也变成了新的函数,所以需要在APP重新渲染的时候保证handleClick是不变的(prop不发生变化,子组件就不会重新渲染)
//使用useCallback来保证函数的不变性
const handleClick = useCallback(() => {
console.log('父组件')
}, [])
const handleUpdata = () => {
setCount(count + 1)
console.log('更新数据')
}
return (
<>
<p>count的值{count}</p>
<button onClick={handleUpdata} >count + 1</button>
<br />
<Button onClick={handleClick} >count + 1</Button>
</>
);
}