import React, { useEffect, useState } from 'react';
- 告别了令人疑惑的生命周期
- 告别类组件中烦人的this
- 告别繁重的类组件,回归到了熟悉的函数组件
useState
1.基础使用
import { useState } from 'react' function App() { // 参数:状态初始值比如,传入 0 表示该状态的初始值为 0 // 返回值:数组,包含两个值:1 状态值(state) 2 修改该状态的函数(setState) const [count, setCount] = useState(0); // 修改count内容 const modifyEvent = () => { setCount(count + 1) } return ( <button onClick={() => modifyEvent()}>{count}</button> ) } export default App
2.状态的读取和修改执行流程与逻辑
1.setCount是一个函数,参数表示最新的状态值
2.调用该函数后,将使用新值替换旧值
3.修改状态后,由于状态发生变化,会引起试图变化 注意
事项:修改状态的时候,一定要使用新的状态替换旧的状态,不能直接修改旧的状态,尤其是引用类型
3. 组件的更新过程
1.组件第一次渲染
- 从头开始执行该组件中的代码逻辑
- 调用 useState(0) 将传入的参数作为状态初始值,即:0
- 渲染组件,此时,获取到的状态 count 值为: 0
2.组件第二次渲染
- 点击按钮,调用 setCount(count + 1) 修改状态,因为状态发生改变,所以,该组件会重新渲染
- 组件重新渲染时,会再次执行该组件中的代码逻辑
- 再次调用 useState(0) ,此时 React 内部会拿到最新的状态值而非初始值,比如,该案例中最新的状态值为 1
- 再次渲染组件,此时,获取到的状态 count 值为:1
import { useState } from 'react' function App() { const [count, setCount] = useState(0) // 在这里可以进行打印 console.log(count,'渲染了') return ( <button onClick={() => { setCount(count + 1) }}>{count}</button> ) } export default App
4.使用规则
function List(){ // 以字符串为初始值 const [name, setName] = useState('cp') // 以数组为初始值 const [list,setList] = useState([]) }
2.useState 注意事项
- 只能出现在函数组件或者其他hook函数中
- 能嵌套在if/for/其它函数中(react按照hooks的调用顺序识别每一个hook)
useEffect
1.理解函数副作用
- 数据请求 ajax发送
- 手动修改dom
- localstorage操作
2. 基础使用
下面案例说明,函数组件储存了当前state所有状态,函数初次就会触发他们两个的加载,另外当某一个发生改变了 useEffect和函数都会被重新执行加载
import { useEffect, useState } from 'react' function App() { const [count, setCount] = useState(0) useEffect(()=>{ // 修改了dom数据后 userffect函数被副作用重新执行 console.log('执行了副作用函数') }); // 函数组件也会被重新执行 console.log('函数组件被重新执行了') return ( <button onClick={() => { setCount(count + 1) }}>{count}</button> ) } export default App
3.useEffect依赖项和控制执行的时机
import { useEffect, useState } from 'react' function App() { const [count, setCount] = useState(0) useEffect(()=>{ // 修改了dom数据后 userffect函数不会在被触发,只有首次加载函数才会执行一次 console.log('执行了副作用函数') },[]); //修改了dom数据后 函数组件会被重新执行 console.log('函数组件被重新执行了') return ( <button onClick={() => { setCount(count + 1) }}>{count}</button> ) } export default App
添加特定项作为依赖
副作用函数在首次渲染时执行,在依赖项发生变化时重新执行
给useEffect添加特定的依赖项,当这个依赖性的state发生改变,useEffect与函数组件都会重新渲染被执行,由于第二个参数是依赖项所以是数组可以添加多个依赖项
没有useEffect添加的特定的依赖项,就不会触发useEffect函数,只会触发组件的渲染函数
function App() { const [count, setCount] = useState(0) const [name, setName] = useState('zs') useEffect(() => { console.log('副作用执行了') }, [count]) console.log('组件被执行了') return ( <> <button onClick={() => { setCount(count + 1) }}>{count}</button> <button onClick={() => { setName('cp') }}>{name}</button> </> ) }
清理副作用
function App() { const [count, setCount] = useState(0) const [name, setName] = useState('zs') useEffect(() => { console.log('副作用执行了') return () => { alert(1) console.log('执行了清楚副作用,组件卸载的时候执行') } }, [count]) console.log('组件被执行了') return ( <> <button onClick={() => { setCount(count + 1) }}>{count}</button> <button onClick={() => { setName('cp') }}>{name}</button> </> ) }
useMemo(性能优化)
解决函数组件的性能问题,比如子组件重复执行问题,每次渲染都进行高开销的计算
// 子组件 function Sub(props) { console.log("Sub render"); let { number, onClick } = props return ( <button onClick={onClick}>{number}</button> ) } // 父组件 function Test() { let [value, setValue] = useState('') let [number, setNumber] = useState(0) const addClick = () => setNumber(number + 1) return <> <input type="text" value={value} onChange={(e) => setValue(e.target.value)} /> <Sub number={number} onClick={addClick} /> </> } export default Test;
子组件依赖的只有number ,理想情况下只希望number变化时触发子组件重新渲染
但实际是在输入框内的值发生变化,子组件也会重新渲染 如果子组件的逻辑较复杂,就是无意义的大量计算,浪费资源
// 子组件 function Sub(props) { console.log("Sub render"); let { number, onClick } = props return ( <button onClick={onClick}>{number}</button> ) } // 父组件 function Test() { let [value, setValue] = useState('') let [number, setNumber] = useState(0) const addClick = () => setNumber(number + 1); // 使用useMemo记住计算后的值,只有当依赖number变量发生变化,才会重新计算子组件内容 const MemoSub = useMemo( () => <Sub data={number} onClick={addClick} />, [number] // 只有 number 变化才重新计算 MenoSub ) return <> <input type="text" value={value} onChange={(e) => setValue(e.target.value)} /> {MemoSub} </> } export default Test;
useCallback(性能优化)
接收两个参数:回调函数和依赖项数组。回调函数是需要缓存的函数,依赖项数组用于确定何时需要重新创建函数实例。
当依赖项数组中的任何一个值发生变化时,useCallback 将返回一个新的函数实例,否则它将返回之前缓存的函数实例
import { useState, useCallback } from 'react'; function MyComponent() { const [count, setCount] = useState(0); // 使用 useCallback 缓存 handleClick 函数 const handleClick = useCallback(() => { setCount(count + 1); }, [count]); return ( <div> <p>You clicked {count} times</p> {/* 在按钮上使用缓存的 handleClick 函数 */} <button onClick={handleClick}>Click me</button> </div> ); }
在这个例子中,我们使用 useCallback 来缓存回调函数 handleClick, 将其缓存以避免在每次重新渲染组件时创建新的函数实例。
同时,在按钮上使用了缓存的 handleClick 函数,以确保点击按钮时调用的是缓存的函数实例。我们还将 count 添加到依赖项数组中,以确保每当 count 发生变化时,handleClick 都会被重新创建。
useCallback 和 useMomeo 的区别
1.useCallback 和 useMemo 都是用于性能优化的 React 钩子函数,它们都可以避免不必要的重新计算或重新渲染。虽然它们看起来很相似,但它们有几个重要的区别。
2.首先,useCallback 返回一个缓存的回调函数,而 useMemo 返回一个缓存的值。这意味着 useCallback 的主要作用是为一个函数创建缓存,而 useMemo 的主要作用是缓存一个值
3.最后,它们的使用场景也不同。useCallback 适用于优化回调函数,避免不必要的重新渲染,并传递给子组件。而 useMemo 适用于优化计算开销较大的值,如大型数组或对象的计算
useRef
useRef 可以缓存所有数据类型,更新的数据不会随着组件的重新渲染而重置,会一直保持最新状态的内容,
但是保存的数据类型 无法在ui渲染页面上使用,只能作为一个状态进行储存
也可以绑定给一个元素标签获取dom进行操作
function Test() { /* 保存 DOM */ const inputEl = useRef() const onClick = () => { console.log(inputEl); // 对象类型,只有一个 current 属性指向指定DOM inputEl.current.innerHTML = '2asdasd sd阿萨德' } return <div> <div ref={inputEl}></div> <button onClick={onClick}>click me!!!</button> <br /> </div> }
useContext
usecontext React 16.3本中新引入的一个特性,它可以让组件之间共享数据十分方便。它属于 React Context API(上下文API),可以让组件层级之间自由传递数据,而使用Context API可以极大地提高组件之间的可复用性。
使用 useContext以使组件树中的任何组件访问到 cntext值,无论它是何种层级的,而且更方便的是,不需要利用props行传递,而只需要一行代码即可
下面案例是同一组件下多个 子组件的上下文通讯
import React, { createContext, useContext } from 'react'; const ThemeContext = createContext('light') // 父组件 function Test() { return ( <ThemeContext.Provider value="dark"> <Toolbar theme="dark" /> </ThemeContext.Provider> ) } // 子组件 function Toolbar(props) { return ( // 中间的组件再也不必指明往下传递 theme 了。 <div> <Button /> </div> ) } // 子组件中的子组件 function Button() { // 指定 contextType 读取当前的 themecontext。 // React 会往上找到最近的 theme Provider,然后使用它的值。 // 在这个例子中,当前的 theme 值为 “dark”。 const theme = useContext(ThemeContext) return <button>{ theme }</button> }
垮文件使用usecontext 进行通讯
父组件
import React, { useState, createContext } from 'react' import Counter from './Counter' export const countContext = createContext() export default function Example4() { const [count, setCount] = useState(0) return ( <div> <p>你点击了{count}次</p> <button onClick={()=>{setCount(count+1)}}>点击</button> <countContext.Provider value={count}> <Counter /> </countContext.Provider> </div> ) }
子组件
import React, { useContext } from 'react' import { countContext } from './Example4' function Counter() { let count = useContext(countContext) return ( <div> <h2>{count}</h2> </div> ) } export default Counter
这样在多级组件使用就减少使用redux,useContext确实好用,起码跨级传递数据不用那么麻烦了
完结,有不足欢迎补充。