建议直接看官网:https://zh-hans.react.dev/reference/react/useState
在组件的顶层调用 useState 来声明一个 状态变量。如果没有传递参数,那么初始化值为undefined。
useState 返回一个由两个值组成的数组:
const [state, setState] = useState(initialState)
useEffect 可以让你来完成一些类似于class中生命周期的功能;
useEffect 是一个 React Hook,它允许你 将组件与外部系统同步。
useEffect(setup, dependencies?)
Hook 允许我们按照代码的用途分离出多个useEfffect , 而不是像生命周期函数那样
创建一个干净的脚手架
在src 文件夹下创建 Son.jsx 文件,用于创建子组件
import React, { memo, useEffect, useState } from 'react';
const Son = (props) => {
const [conter, setConter] = useState(0);
const [msg, setMsg] = useState('一条信息');
// 每个 useEffect 对应一个功能
// 每次渲染时执行
useEffect(() => {
console.log(props);
console.log('Son组件挂载了');
const timer = setInterval(() => {
console.log('Son组件内部定时器');
}, 1000);
// 组件卸载时清除定时器,(类似componentWillUnmount 生命周期函数)
// 如果不清除,定时器会一直存在
return () => {
timer && clearInterval(timer);
console.log('Son组件卸载了');
};
});
// 每次渲染时执行
useEffect(() => {
document.title = conter;
});
// 回调函数只在 msg 变化时执行,
// 不受别的变量的影响
useEffect(() => {
console.log('msg 被修改');
}, [msg]);
// 不受任何的变量的影响,只执行一次
// 类似 componentDidMount 生命周期函数
useEffect(() => {
console.log('不受任何的变量的影响');
}, []);
return (
<div>
<p>{props.title}</p>
Son:{conter}
<p>{msg}</p>
<button onClick={(e) => setConter(conter + 1)}>增加 + 1</button>
<button onClick={(e) => setMsg('另一个消息')}>修改 msg</button>
</div>
);
};
export default Son;
修改sr/App.js 文件内容如下
import React, { memo, useState } from 'react';
import Son from './Son';
const App = () => {
const [flag, setFlag] = useState(true);
const [title, setTitle] = useState('标题');
const changeFlag = () => {
setFlag(!flag);
};
const changeTitle = () => {
setTitle('标题2');
};
return (
<div>
{flag && <Son title={title} />}
<button onClick={changeFlag}>销毁/挂载子组件</button>
{/* 修改 title 后 Son组件将会被销毁并重新挂载 */}
<button onClick={changeTitle}>修改title</button>
</div>
);
};
export default App;
useContext 是一个 React Hook,可以让你读取和订阅组件中的 context。
const value = useContext(SomeContext)
创建一个干净的脚手架
在 src 文件夹下创建 context.js 文件,用于存放共用信息
import React from 'react';
export const themes = {
light: {
foreground: '#000000',
background: '#eeeeee',
color: '#000',
},
dark: {
foreground: '#ffffff',
background: '#222222',
color: '#fff',
},
};
// 创建context对象的
export const ThemeContext = React.createContext();
export const user = {
id: '001',
name: '张三',
age: 18,
gender: '男',
};
// 创建context对象的, 将 user 作为默认值
export const UserContext = React.createContext(user);
在src 文件夹下创建 User.jsx 文件,用于展示用户信息
import React, { memo, useContext } from 'react';
import { UserContext, ThemeContext } from './context';
const User = memo(() => {
// user 使用的是 createContext 设置的默认值
const user = useContext(UserContext);
// theme 使用的是 ThemeContext.Provider 设置的默认值
const theme = useContext(ThemeContext);
return (
<div
style={{
color: theme.color,
background: theme.background,
}}
>
<p>姓名:{user.name}</p>
<p>年龄:{user.age}</p>
</div>
);
});
export default User;
修改App.js 文件如下
import React, { memo } from 'react';
import { themes, ThemeContext } from './context';
import User from './User';
const App = memo(() => {
return (
<div>
{/* 给 ThemeContext 设置值*/}
<ThemeContext.Provider value={themes.dark}>
<User />
</ThemeContext.Provider>
</div>
);
});
export default App;
useCallback实际的目的是为了进行性能的优化。
useCallback 是一个允许你在多次渲染中缓存函数的 React Hook。
const cachedFn = useCallback(fn, dependencies)
useCallback会返回一个函数的 memoized(记忆的) 值;
在依赖不变的情况下,多次定义的时候,返回的值是相同的;
通常使用useCallback的目的是不希望子组件进行多次渲染,并不是为了函数进行缓存
import React, { memo, useCallback, useState } from 'react';
// 创建一个子组件
const Child = memo((props) => {
console.log('Child 组件重新渲染了');
const { customChangeCounter } = props;
return (
<div>
Child
<button onClick={customChangeCounter}>修改counter</button>
</div>
);
});
// 每次 counter 改变时,会重新渲染整个 App 组件
const App = memo(() => {
const [counter, setCounter] = useState(0);
const [name, setName] = useState('张三');
console.log('App 组件重新渲染了');
function changeName() {
setName('李四');
}
// 每次 counter 改变时,会重新渲染整个 App 组件
// changeCounter1 也会被重新定义
function changeCounter1() {
console.log('changeCounter1被定义');
setCounter(counter + 1);
}
// 如果没有第二个参数,和上边的 changeCounter1 没有区别
// 第二个参数是依赖项,只有依赖项改变时,useCallback 返回值才会是一个新的函数
// 注:App 组件重新渲染时 useCallback 的第一个参数会被重新定义,
// 只是 useCallback 返回值会根据依赖项改变而改变
const changeCounter2 = useCallback(() => {
console.log('changeCounter2被定义');
setCounter(counter + 1);
console.log(counter);
}, [counter]);
// 依赖项不改变,useCallback 返回值不会改变
const changeCounter3 = useCallback(() => {
console.log('changeCounter3被定义');
// counter 将不会改变
setCounter(counter + 1);
console.log(counter);
}, []);
return (
<div>
<p>{counter}</p>
<p>{name}</p>
{/**
* 1.customChangeCounter 传递changeCounter1 时,修改name 后,Child1 也会重新渲染
* 因为 name 被修改后 App 组件重新渲染, changeCounter1 被重新定义,
* customChangeCounter 获取到一个新值,所以 Child1 重新渲染
* 2.customChangeCounter 传递changeCounter2 时,修改name 后,Child1 不重新渲染
* 因为 counter 没有改变,changeCounter2 没有被重新定义,
*/}
<Child customChangeCounter={changeCounter2} />
<button onClick={() => changeCounter1()}>button1:+1</button>
<button onClick={() => changeCounter2()}>button2:+1</button>
<button onClick={() => changeCounter3()}>button3:+1</button>
<button onClick={changeName}>修改name</button>
</div>
);
});
export default App;
useMemo 是一个 React Hook,它在每次重新渲染的时候能够缓存计算的结果。
在依赖不变的情况下,多次定义的时候,返回的值是相同的;
const cachedValue = useMemo(calculateValue, dependencies)
import React, { memo, use, useMemo } from 'react';
// 子组件
const Child = memo((props) => {
console.log('Child 被渲染');
const { user } = props;
return (
<div>
{user.name}-{user.age}
</div>
);
});
// 计算属性
const countTotal = (count) => {
let total = 0;
console.log('countTotal 被调用');
for (let i = 1; i <= count; i++) {
total += i;
}
return total;
};
const App = memo(() => {
console.log('App 被渲染');
const [count, setCount] = React.useState(0);
const [user, setUser] = React.useState({ name: '张三', age: 18 });
const changeUser = () => {
setUser({ name: '李四', age: 20 });
};
/**
* user 改变时,会重新渲染整个 App 组件
* countTotal 也会被重新调用
*/
// const total = countTotal(count);
// 只有 count 改变时才会重新调用 countTotal
const total2 = useMemo(() => {
return countTotal(count);
}, [count]);
return (
<div>
<p>{count}</p>
<p>{total2}</p>
<Child user={user} />
{/* 修改 count, 子组件没有被重新渲染 */}
<button onClick={() => setCount(count + 1)}>+1</button>
<button onClick={changeUser}>修改 User</button>
</div>
);
});
export default App;
useRef 是一个 React Hook,它能帮助引用一个不需要渲染的值。
useRef返回一个ref对象,返回的ref对象再组件的整个生命周期保持不变
const ref = useRef(initialValue)
import React, { memo, useCallback, useRef } from 'react';
const App = memo(() => {
const [count, setCount] = React.useState(0);
console.log('App 被渲染');
// 用法1:绑定一个 Dom
const titleDom = useRef();
const showTitleDom = () => {
console.log(titleDom.current);
};
// useCallback 没有依赖,所以回调函数不会重新生成
// 即使 count 改变,App 重新渲染,回调函数也不会重新生成
// 回调函数不重新生成, 回调函数内部的count,只会是第一次渲染时的值
const addCount = useCallback(() => {
// count 会一直都是 0
setCount(count + 1);
}, []);
// 用法2:绑定一个值
const countRef = useRef()
countRef.current = count;
const addCount2 = useCallback(() => {
// count 会一直都是 0
setCount(countRef.current + 1);
}, []);
return (
<div>
{count}
<h2 ref={titleDom}>标题</h2>
<button onClick={showTitleDom}>显示标题Dom</button>
<button onClick={addCount}>增加Count</button>
<button onClick={addCount2}>增加Count2</button>
</div>
);
});
export default App;
useImperativeHandle(ref, createHandle, dependencies?)
import React, { forwardRef, memo, useImperativeHandle, useRef } from 'react';
// 子组件1
const Child1 = memo(forwardRef((props, ref) => {
return (
<div>
<input ref={ref} type="text" />
</div>
);
}));
// 子组件2
const Child2 = memo(forwardRef((props, ref) => {
const inputRef = useRef();
// 父组件通过ref,调用子组件的方法
// 只能使用 useImperativeHandle,第二个参数所暴露的方法
useImperativeHandle(ref, () => ({
// 只暴露聚焦方法
focus: () => {
console.log(inputRef.current);
inputRef.current.focus();
},
}));
return (
<div>
<input ref={inputRef} type="text" />
</div>
);
}));
const App = memo(() => {
const childRef1 = useRef();
const childRef2 = useRef();
return (
<div>
<Child1 ref={childRef1} />
<Child2 ref={childRef2} />
<button onClick={() => childRef1.current.focus()}>聚焦Child1</button>
<button onClick={() => childRef1.current.value = 2}>修改值Child1</button>
<button onClick={() => childRef2.current.focus()}>聚焦Child2</button>
<button onClick={() => childRef2.current.value = 2}>修改值Child2</button>
</div>
);
});
export default App;
useLayoutEffect 可能会影响性能。尽可能使用 useEffect。
useLayoutEffect看起来和useEffect非常的相似,事实上他们也只有一点区别而已:
useLayoutEffect 是 useEffect 的一个版本,在浏览器重新绘制屏幕之前触发。
useLayoutEffect(setup, dependencies?)
import React, { memo, useState, useEffect, useLayoutEffect } from 'react';
const App = memo(() => {
const [count, setCount] = useState(5);
// 会有闪烁现象
useEffect(() => {
console.log('useEffect');
if (count === 0) {
setCount(Math.random() + 88);
}
});
// useLayoutEffect(() => {
// console.log('useEffect');
// if (count === 0) {
// setCount(Math.random() + 88);
// }
// });
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(0)}> count 设置为0</button>
</div>
);
});
export default App;