useState
模拟实现
因为是根据数组的索引来储存数据,所以不能在 if 里使用 useState,这样会导致数据顺序错乱。
let _state = [] // 将设置的值保存在函数外let _index = 0 // 储存多个值function myUseState(initialValue) {const currentIndex = indexindex += 1_state[currentIndex] = _state[currentIndex] === undefined ? initialValue : _state // 没设置值的时候取初始值const setState = (newValue) => {_state = newValueReactDOM.render(index = 0<App/>,document.getElementById('root')) // 重新渲染}return [_state[currentIndex], setState] // 返会 值和 setter 函数}
App 组件的更新过程
注意事项
每次重新渲染,函数会重新执行,对应的 state 会出现分身。如果不希望出现可以使用 useRef 或 useContext
useRef
作用
useRef返回一个可变的 ref 对象,.current该对象的属性已初始化为传递的参数(initialValue)。返回的对象将在组件的整个生命周期内保持不变。本质上,
useRef就像一个“盒子”,可以在其.current属性中保存可变的值。语法
function App() {const value = useRef(0)const onClick = () => value.current += 1 // current 为了保证不同组件使用的是同一个值(引用)useEffect(() => { // 挂载后执行 log valueconsole.log(value.current) // <input type="text">})return (<><input ref={value} type="text"/><button onClick={onClick}>+1</button></>)
注意事项
不会触发 UI 更新
不能将
ref传递给子组件。因为props中没有ref,此时需要使用 forwarfRef APIforWardRef
作用
创建一个 React 组件,这个组件能够将其接受的
ref属性转发到其组件树下的另一个组件中语法
const FancyButton = React.forwardRef((props, ref) => (<div ref={ref} >xxx</div>));// 现在可以直接获得 DOM 的引用:const ref = useRef(null);<FancyButton ref={ref}>Click me!</FancyButton>;
useImperativeHandle
作用
在使用
ref时自定义暴露给父组件的实例值。在大多数情况下,应当避免使用 ref 这样的命令式代码。应当与 forwardRef 一起使用
语法
应该叫做 setRef
useImperativeHandle( ref, createHandle, [deps] )示例
Child 的父组件可以调用
inputRef.current.focus()。function Child(props, ref) {const inputRef = useRef();useImperativeHandle(ref, () => ({ // 修改 reffocus: () => { // 在 ref 上增加 focus 方法inputRef.current.focus();},inputRef:inputRef // 原来的 ref 值}));return <input ref={inputRef} ... />;}FancyInput = forwardRef(FancyInput);
useEffect
Effect(副作用):改变当前了环境,数据获取,设置订阅以及手动更改 React 组件中的 DOM 都属于副作用。
作用
通过使用这个 Hook,你可以告诉 React 组件需要在渲染后执行某些操作。React 会保存你传递的函数(我们将它称之为 “effect”),并且在执行 DOM 更新之后调用它。在这个 effect 中,我们设置了 document 的 title 属性,不过我们也可以执行数据获取或调用其他命令式的 API。
对应类组件中的生命周期
```javascript useEffect( () => {console.log(1)},[] ) ===== componentDidMount // 只在挂载时触发
useEffect( () => {console.log(1)},[n] ) ===== componentDidUpdate
// 第一次也会执行,只在 n 更新时更新,不写表示监听 state props
useEffect( () => { ===== componentWillUnmount // 组件消亡时触发 console.log(1) return () => {} } )
<a name="hwSXI"></a>### 注意事项:在组件内部调用 useEffect- 将 `useEffect` 放在组件内部让我们可以在 effect 中直接访问 `count` state 变量(或其他 props)。我们不需要特殊的 API 来读取它 —— 它已经保存在函数作用域中。Hook 使用了 JavaScript 的闭包机制,而不用在 JavaScript 已经提供了解决方案的情况下,还引入特定的 React API。<a name="marAp"></a># useLayoutEffect<a name="aYXQq"></a>### 作用- 在 `render` 之前调用,执行时机早于 `useEffect`。先用 `useEffect`,只有当它出问题的时候再尝试使用 `useLayoutEffect`。<a name="SjEFF"></a>### 注意事项- 会影响页面渲染速度,最好只在改变 Layout 时使用<a name="c4683"></a># useContext<a name="mcncU"></a>### 语法- 接收一个 context 对象(`React.createContext` 的返回值)并返回该 context 的当前值。当前的 context 值由上层组件中距离当前组件最近的 `<MyContext.Provider>` 的 `value` 属性 决定。- **context:上下文(程序运行时所有可用的变量),局部的全局变量**- 当组件上层最近的 `<MyContext.Provider>` 更新时,该 Hook 会触发重渲染,并使用最新传递给 `MyContext` provider 的 context `value` 值。即使祖先使用 [`React.memo`](https://zh-hans.reactjs.org/docs/react-api.html#reactmemo) 或 [`shouldComponentUpdate`](https://zh-hans.reactjs.org/docs/react-component.html#shouldcomponentupdate),也会在组件本身使用 `useContext` 时重新渲染。<a name="FxYg8"></a>### 使用流程1. 使用` C= createcontext(initial)` 创建上下文1. 使用 `<C.provider></C.Provider>` 圈定作用域1. 在作用域内使用 `useContext(C)` 来使用上下文<a name="05ZyJ"></a>### 作用- 可在祖先、后代组件中传递数据<a name="57jOM"></a>### 注意事项- 不是响应式<a name="WN42v"></a>### 示例- 示例一:提供 set 数据```javascriptconst themes = {light: {foreground: "#000000",background: "#eeeeee"},dark: {foreground: "#ffffff",background: "#222222"}};const ThemeContext = React.createContext(themes.light); // 创建function App() {return (<ThemeContext.Provider value={themes.dark}> // 在此作用域中可以使用 useContext<Toolbar /></ThemeContext.Provider>);}function Toolbar(props) { // 子组件return (<div><ThemedButton /></div>);}function ThemedButton() { // 孙子组件const theme = useContext(ThemeContext); // 注入(使用),值为最近的<ThemeContext.Provider> 的值return (<button style={{ background: theme.background, color: theme.foreground }}>使用主题</button>);}
- 示例二:提供 set、get 数据
```javascript
const ThemeContext = React.createContext(null) // 创建
function App() { // 祖先组件
const [theme, setTheme] = useState({ light: {
}, }) return (foreground: '#000000',background: '#eeeeee'
// 提供 set、get ) }<Toolbar/>
function Toolbar(props) { // 子组件 return (
function ThemedButton() { // 孙子组件
const {theme, setTheme} = useContext(ThemeContext) // 注入(使用),值为最近的
<a name="1AJNb"></a># useReducer<a name="1RRG9"></a>### 语法- useReducer 接收一个形如 `(state, action) => newState` 的 reducer,并返回当前的 state 以及与其配套的 `dispatch` 方法。<a name="cSqwr"></a>### 作用- state 逻辑较复杂且包含多个子值,或下一个 state 依赖于之前的 state 等情况,`useReducer` 比 `useState` 更适用。```javascriptconst initialState = {count: 0};function reducer(state, action) { // 触发时会将会执行 reducer,并将参数和初始值穿过来switch (action.type) {case 'increment':return {count: state.count + 1};case 'decrement':return {count: state.count - 1};default:throw new Error();}}function Counter() {const [state, dispatch] = useReducer(reducer, initialState);// 接受 reducer 和初始值,返回 state 和 触发 Apireturn (<>Count: {state.count}<button onClick={() => dispatch({type: 'decrement'})}>-</button> // 接受一个对象传给reducer<button onClick={() => dispatch({type: 'increment'})}>+</button></>);}
memo
作用
如果 props 没有改变,会跳过渲染并直接复用最近一次渲染的结果,避免重复渲染
语法
const fn = memo(() => {} )接受一个组件函数示例
不使用 memo 时,n 变化时 App 组件的所有子组件都会重新渲染 ```javascript function App() { const [n, setN] = useState(0) const [m, setm] = useState(0) const add = () => setN(n + 1) return (
) }<div><button onClick={add}>+1</button></div><Child data={m}/>
/ function Child(props) { console.log(‘child 渲染了’) return
<a name="SZf5z"></a>### 注意事项- 当子组件 props 中有函数,父组件重新渲染时会重新生成函数,此时函数地址变化,会导致 memo 失效,需使用 [**useMemo **](#hTQyT)API<a name="hTQyT"></a># useMemo<a name="8TrtL"></a>### 作用- 把“创建”函数和依赖项数组作为参数传入 `useMemo`,它会在有依赖项改变时才重新计算 memoized(记忆的) 值。这种优化有助于避免在每次渲染时都进行高开销的计算。<a name="SHRuQ"></a>### 语法- `const fn = memo(() => (x)=>{},[xxx])` 接受一个函数和依赖项数组```javascriptfunction App() {const [n, setN] = useState(0)const [m, setm] = useState(0)const add = () => setN(n + 1)//const fn = () => console.log(123) 使用 useMemoconst fn = useMemo(() => () => console.log(123), [m])return (<div><div><button onClick={add}>+1</button></div><Child data={m} fn={fn()}/></div>)}const Child = memo((props) => {console.log('child 渲染了')return <div onClick={props.fn}>{props.data}</div>})
注意事项
- 语法过于复杂,于是创造了语法糖 useCallback
useCallback
useCallback( fn, deps ) 等同于 useMemo( () => fn, deps )

