
Hooks的本质:一套能够使函数组件更强大,更灵活的“钩子”

 注意点:
Hooks的出现解决了俩个问题 1. 组件的状态逻辑复用 2.class组件自身的问题

作用:
useState为函数组件提供状态(state)
使用步骤:
代码实现:
import {
     useState } from 'react'
function App() {
    
  // 参数:状态初始值比如,传入 0 表示该状态的初始值为 0
  // 返回值:数组,包含两个值:1 状态值(state) 2 修改该状态的函数(setState)
  const [count, setCount] = useState(0)
  return (
    <button onClick={
    () => {
     setCount(count + 1) }}>{
    count}</button>
  )
}
export default App
 
 读取状态
该方式提供的状态,是函数内部的局部变量,可以在函数内的任意位置使用
修改状态
注意事项

函数组件使用 useState hook 后的执行过程,以及状态值的变化
重点一句话:useState 的初始值(参数)只会在组件第一次渲染时生效。也就是说,以后的每次渲染,useState 获取到都是最新的状态值,React 组件会记住每次最新的状态值
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
 
 useState 函数可以执行多次,每次执行互相独立,每调用一次为函数组件提供一个状态 (也就是每个状态互不影响)
function List(){
      
  // 以字符串为初始值
  const [name, setName] = useState('cp')
  // 以数组为初始值
  const [list,setList] = useState([])
}
 useState 注意事项
let num = 1
function List(){
        
  num++
  if(num / 2 === 0){
        
     const [name, setName] = useState('cp') 
  }
  const [list,setList] = useState([])
}
// 俩个hook的顺序不是固定的,这是不可以的!!!
 使用场景
如果初始 state 需要通过计算才能获得,则可以传入一个函数。参数(也就是回调函数)只会在组件的初始渲染中起作用,后续渲染时会被忽略。
语法:
const [name, setName] = useState(()=>{
       
  // 编写计算逻辑    return '计算之后的初始值'
})
 
 语法规则
语法选择
 3. 如果就是初始化一个普通的数据 直接使用 useState(普通数据) 即可
 4. 如果要初始化的数据无法直接得到需要通过计算才能获取到,使用useState(()=>{})
例如我们来一个需求:
 
import {
     useState } from 'react'
function Counter(props) {
    
  const [count, setCount] = useState(() => {
    
    return props.count
  })
  return (
    <div>
      <button onClick={
    () => setCount(count + 1)}>{
    count}</button>
    </div>
  )
}
function App() {
    
  return (
    <>
      <Counter count={
    10} />
      <Counter count={
    20} />
    </>
  )
}
export default App
 
 
什么是副作用?
副作用是相对于主作用来说的,一个函数除了主作用,其他的作用就是副作用。对于 React 组件来说,主作用就是根据数据(state/props)渲染 UI,除此之外都是副作用(比如,手动修改 DOM)
常见的副作用
useEffect函数的作用就是为react函数组件提供副作用处理的!
你可以把useEffect看作是React生命周期方法的一种替代,useEffect可以模拟componentDidMount,componentDidUpdate,和componentWillUnmount的行为。你可以在一个组件中使用多个useEffect,以便将不同的副作用分开处理。useEffect是一个Hook,所以你只能在组件的顶层或者你自定义的Hook中调用它,不能在循环或条件语句中调用它。如果你不需要与外部系统同步,你可能不需要使用useEffect。
作用
为react函数组件提供副作用处理
使用步骤
import {
     useEffect, useState } from 'react'
function App() {
    
  const [count, setCount] = useState(0)
 
  useEffect(()=>{
    
    // dom操作
    document.title = `当前已点击了${
      count}次`
  })
  return (
    <button onClick={
    () => {
     setCount(count + 1) }}>{
    count}</button>
  )
}
export default App
 
 1.不添加依赖项
组件首次渲染执行一次,以及不管是哪个状态更改引起组件更新时都会重新执行
useEffect(()=>{
    
    console.log('副作用执行了')
})
 
 2.添加空数组
组件只在首次渲染时执行一次
useEffect(()=>{
    
	 console.log('副作用执行了')
},[])
 
 3.添加特定依赖项
副作用函数在首次渲染时执行,在依赖项发生变化时重新执行
function App() {
      
    const [count, setCount] = useState(0)  
    const [name, setName] = useState('zs') 
    
    useEffect(() => {
        
        console.log('副作用执行了')  
    }, [count])  
    
    return (    
        <>      
         <button onClick={
    () => {
     setCount(count + 1) }}>{
    count}</button>      
         <button onClick={
    () => {
     setName('cp') }}>{
    name}</button>    
        </>  
    )
}
 
 注意事项:
如果想要清理副作用 可以在副作用函数中的末尾return一个新的函数,在新的函数中编写清理副作用的逻辑。
注意执行时机为:
import {
     useEffect, useState } from "react"
const App = () => {
    
  const [count, setCount] = useState(0)
  useEffect(() => {
    
    const timerId = setInterval(() => {
    
      setCount(count + 1)
    }, 1000)
    return () => {
    
      // 用来清理副作用的事情
      clearInterval(timerId)
    }
  }, [count])
  return (
    <div>
      {
    count}
    </div>
  )
}
export default App
 
 使用场景
如何在useEffect中发送网络请求,并且封装同步 async await操作
语法要求
useEffect中不能是一个异步函数的原因是,useEffect的第一个参数应该是一个返回undefined或者一个清理函数的函数,而不是一个返回Promise的函数。异步函数会返回一个Promise,这个Promise不能被当作一个清理函数来调用。这样做会导致useEffect的行为不符合预期,可能会出现内存泄漏或者数据不一致的问题。

正确写法
如果你想在useEffect中使用异步函数,你有两种方法:
useEffect(() => {
    
  // 定义一个异步函数
  const fetchData = async () => {
    
    // 你可以在这里使用await
    const response = await MyAPI.getData(someId);
    // ...
  }
  // 立即调用这个异步函数
  fetchData();
}, [someId]); // 或者[]如果副作用不需要props或state
 
 // 在useEffect外部定义一个异步函数,并用useCallback包裹它
const fetchData = useCallback(async () => {
    
  // 你可以在这里使用await
  const response = await MyAPI.getData(someId);
  // ...
}, [someId]); // 这里的依赖项要和useEffect的一致
useEffect(() => {
    
  // 在useEffect内部调用这个异步函数
  fetchData();
}, [fetchData]); // 这里的依赖项要包含这个异步函数
 
 
使用场景
在函数组件中获取真实的dom元素对象或者是组件对象
使用步骤
代码实现:
获取dom
import {
     useEffect, useRef } from 'react'
function App() {
      
    const h1Ref = useRef(null)  
    useEffect(() => {
        
        console.log(h1Ref)  
    },[])  
    return (    
        <div>      
            <h1 ref={
     h1Ref }>this is h1</h1>    
        </div>  
    )
}
export default App
 
 获取组件实例
函数组件由于没有实例,不能使用ref获取,如果想获取组件实例,必须是类组件
class Foo extends React.Component {
      
    sayHi = () => {
        
        console.log('say hi')  
    }  
    render(){
        
        return <div>Foo</div>  
    }
}
    
export default Foo
 
 import {
     useEffect, useRef } from 'react'
import Foo from './Foo'
function App() {
      
    const h1Foo = useRef(null)  
    useEffect(() => {
        
        console.log(h1Foo)  
    }, [])  
    return (    
        <div> <Foo ref={
     h1Foo } /></div>  
    )
}
export default App
 
 

 UseContext的作用是让你在组件中使用Context,Context是一种在组件树中传递数据的方式,它可以让你在不使用props的情况下,让深层嵌套的组件访问一些全局的状态。使用UseContext的步骤如下:
使用UseContext的好处是,你不需要通过props来一层一层地传递数据,这样可以避免一些不必要的渲染和复杂的逻辑。你也可以在Context中存储一些函数,以便在组件中调用它们。使用UseContext的注意事项是,你需要保证你使用的Context对象是同一个,否则你可能会得到undefined的值。你也需要注意useContext的依赖关系,如果你的Context的value发生变化,那么所有使用了这个Context的组件都会重新渲染,除非你使用了memo或者useMemo来优化性能。
代码实现:
import {
     createContext, useContext } from 'react'
// 1.创建Context对象
const Context = createContext()
function Foo() {
      
    return <div>Foo <Bar/></div>
}
function Bar() {
      
    // 3.底层组件通过useContext函数获取数据  
    const name = useContext(Context)  
    return <div>Bar {
    name}</div>
}
function App() {
      
    return (    
        // 2.顶层组件通过Provider 提供数据    
        <Context.Provider value={
    'this is name'}>     
            <div><Foo/></div>    
        </Context.Provider>  
    )
}
export default App
 
 同时我们们还可以使用Context机制完成依赖注入
Context是React提供的一种在组件树间传递数据的方法,它可以让我们避免通过中间元素传递props,从而简化组件的结构和逻辑。Context的设计目的是为了共享那些对于一个组件树而言是“全局”的数据,比如主题、语言、用户信息等。
依赖注入是一种编程理念,它可以让我们将依赖的对象或者服务从外部注入到组件中,而不是让组件自己创建或者寻找。依赖注入可以提高代码的可测试性、可重用性和可维护性,同时也降低了组件之间的耦合度。
在React中,我们可以使用Context机制来实现依赖注入的效果,即将需要共享的数据或者逻辑封装在一个Context对象中,然后在组件树的上层使用Provider组件来提供这个Context对象,再在下层的组件中使用Consumer组件或者useContext Hook来消费这个Context对象。这样,我们就可以在不同的组件中访问和操作同一个Context对象,而不需要通过props来传递。
一个简单的例子是,我们可以创建一个ThemeContext对象,用来存储主题相关的数据,比如颜色、字体等。然后在根组件中使用ThemeContext.Provider来提供这个对象,并在子组件中使用ThemeContext.Consumer或者useContext(ThemeContext)来获取这个对象。这样,我们就可以在子组件中根据主题的数据来渲染不同的样式,而不需要从根组件一层一层地传递主题的数据。代码如下:
// 创建一个ThemeContext对象
const ThemeContext = React.createContext({
    
  color: 'black',
  font: 'Arial'
});
// 根组件
function App() {
    
  // 使用ThemeContext.Provider来提供ThemeContext对象
  return (
    <ThemeContext.Provider value={
    {
    color: 'blue', font: 'Helvetica'}}>
      <div className="App">
        <Header />
        <Content />
      </div>
    </ThemeContext.Provider>
  );
}
// 子组件
function Header() {
    
  // 使用useContext(ThemeContext)来获取ThemeContext对象
  const theme = useContext(ThemeContext);
  // 根据ThemeContext对象来渲染样式
  return (
    <div className="Header" style={
    {
    color: theme.color, font: theme.font}}>
      <h1>React Context Example</h1>
    </div>
  );
}
// 子组件
function Content() {
    
  // 使用ThemeContext.Consumer来获取ThemeContext对象
  return (
    <div className="Content">
      <ThemeContext.Consumer>
        {
    theme => (
          // 根据ThemeContext对象来渲染样式
          <p style={
    {
    color: theme.color, font: theme.font}}>
            This is some content with theme.
          </p>
        )}
      </ThemeContext.Consumer>
    </div>
  );
}
 
 注意:
更多【前端框架-React进阶之路(三)-- Hooks】相关视频教程:www.yxfzedu.com