react学习笔记 常用hook学习

一,useState

语法使用

const [参数,修改参数的方法] = useState(初始值)

// 变量 和 修改变量的方法
const [count, setCount] = React.useState(0)
// 定义对象
const [obj, setObj] = React.useState({ name: '张三', age: 18 })
// 定义数组
const [list, setList] = React.useState([])
 举例一
import React from 'react'
export default function App() {
  // 变量 和 修改变量的方法
  const [count, setCount] = React.useState(0)
  const add = () => {
    setCount(count + 1)
  }
  return (
    <div>
      <p>{count}</p>
      <button onClick={add}>+1</button>
    </div>
  )
}

点击按钮页面加一

 举例二 修改对象的某一项值

写一个简单的页面 展示用户数据 添加按钮修改年龄为20岁

错误写法
import React from 'react'
export default function App() {
  // 变量 和 修改变量的方法
  const [user, setUser] = React.useState({ name: 'admin', age: 18 })
  // 修改年龄
  const editUser = () => {
    setUser({ age: 20 })
  }
  return (
    <div>
      <div>姓名:{user.name}</div>
      <div>年龄:{user.age}</div>
      <button onClick={editUser}>修改年龄</button>
    </div>
  )
}

这时候我的eslint其实已经报错了

报错原因

提示了缺少了name属性 其实是因为userState是一个覆盖的操作 在调用setUser的时候需要传递一个对象对原有的对象进行替换

正确写法

可以通过结构赋值的方式进行修改

import React from 'react'
export default function App() {
  // 变量 和 修改变量的方法
  const [user, setUser] = React.useState({ name: 'admin', age: 18 })
  // 修改年龄
  const editUser = () => {
    // 结构出对象中的每一项 然后之后声明的age就能覆盖结构的age
    setUser({ ...user, age: 20 })
  }
  return (
    <div>
      <div>姓名:{user.name}</div>
      <div>年龄:{user.age}</div>
      <button onClick={editUser}>修改年龄</button>
    </div>
  )
}

这时候我们在点击修改就能成功修改了

举例三 如何修改数组的值
错误写法
import React from 'react'
export default function App() {
  // 变量 和 修改变量的方法
  const [user, setUser] = React.useState([
    { name: '小红', age: 18, id: 1 },
    { name: '小明', age: 20, id: 2 },
    { name: '小黄', age: 19, id: 3 }
  ])
  // 新增小白
  const add = () => {
    user.push({ name: '小白', age: 18, id: 4 })
    console.log(user)
    setUser(user)
  }
  return (
    <ul>
      <button onClick={add}>新增一个小白</button>
      {user.map(item => {
        return (
          <li key={item.id}>
            <span>姓名:{item.name}</span>
            <span>年龄:{item.age}</span>
          </li>
        )
      })}
    </ul>
  )
}

错误原因

其实控制台已经打印过了表示的确是push进去了 但是页面并没有更新 是因为我们的数组地址没有发生变化页面监视不到 我们也需要通过传入一个新的数组进行替换

正确写法
import React from 'react'
export default function App() {
  // 变量 和 修改变量的方法
  const [user, setUser] = React.useState([
    { name: '小红', age: 18, id: 1 },
    { name: '小明', age: 20, id: 2 },
    { name: '小黄', age: 19, id: 3 }
  ])
  // 新增小白
  const add = () => {
    setUser([...user, { name: '小白', age: 18, id: user.length + 1 }])
  }
  return (
    <ul>
      <button onClick={add}>新增一个小白</button>
      {user.map(item => {
        return (
          <li key={item.id}>
            <span>姓名:{item.name}</span>
            <span>年龄:{item.age}</span>
          </li>
        )
      })}
    </ul>
  )
}

新增成功

举例四 修改方法可以是一个函数
import React from 'react'

export default function App() {
  const [count, setCount] = React.useState(0)
  const add = () => {
    // count是上一次的值
    setCount(count => {
      return count + 1
    })
  }
  return (
    <div>
      <h1>{count}</h1>
      <button onClick={add}>+1</button>
    </div>
  )
}
举例五 异步执行
import React from 'react'

export default function App() {
  const [count, setCount] = React.useState(0)
  const add = () => {
    // 当我们连续调用setCount时,React会合并调用,只更新一次
    setCount(count + 1)
    setCount(count + 1)
    setCount(count + 1)
  }
  return (
    <div>
      <h1>{count}</h1>
      <button onClick={add}>+1</button>
    </div>
  )
}

并没有加3 是因为react进行判断更改 然后进行合并

通过函数的形式
import React from 'react'
export default function App() {
  const [count, setCount] = React.useState(0)
  const add = () => {
    setCount(count => count + 1)
    setCount(count => count + 1)
    setCount(count => count + 1)
  }
  return (
    <div>
      <h1>{count}</h1>
      <button onClick={add}>+1</button>
    </div>
  )
}

这时候我们点击

页面就变成了3

setTimeout异步包裹
import React from 'react'
export default function App() {
  const [count, setCount] = React.useState(0)
  const add = () => {
    setTimeout(() => {
      setCount(count => count + 1)
      setCount(count => count + 1)
      setCount(count => count + 1)
    })
  }
  // 打印页面渲染次数
  console.log('render')
  return (
    <div>
      <h1>{count}</h1>
      <button onClick={add}>+1</button>
    </div>
  )
}

在react18之之前我们在异步函数中修改几次 就会render几次 但是18之后 在异步函数中也会合并只render一次

怎么强制更新一次
import React from 'react'
import { flushSync } from 'react-dom'
export default function App() {
  const [count, setCount] = React.useState(0)
  const add = () => {
    setTimeout(() => {
      setCount(count => count + 1)
      setCount(count => count + 1)
    })
    // 强行进行一次同步更新
    flushSync(() => {
      setCount(count => count + 1)
    })
  }
  // 打印页面渲染次数
  console.log('render')
  return (
    <div>
      <h1>{count}</h1>
      <button onClick={add}>+1</button>
    </div>
  )
}

这时候能发现render了两次

二,useEffect

useEffect用来模拟渲染后,更新后,销毁这三个动作

语法使用

import React from 'react'
export default function App() {
  const [state, setState] = React.useState(0)
  // useEffect(回调函数,依赖值)
  // dom渲染完成执行
  React.useEffect(() => {}, [])
  // dom渲染完成执行一次 当state发生改变时候执行
  React.useEffect(() => {}, [state])
  // 内部return 函数 在组件销毁时候执行
  React.useEffect(() => {
    return () => {
      console.log('销毁')
    }
  }, [state])
  return <div>App</div>
}
举例一

当我们依赖项为数组的时候 初始化页面执行一次 可以用来获取我们的接口数据

 React.useEffect(() => {
    console.log('dom渲染完成')
    axios.get('/api/user').then(res => {
      setList(res.data)
    })
  }, [])
举例二 

当我们第二个数组中有依赖值的情况下 当依赖值发生改变时候也会执行

import React from 'react'
export default function App() {
  // 控制state始终是count的十倍
  const [count, setcount] = React.useState(1)
  const [state, setState] = React.useState(0)
  // 监听count的变化,将count*10赋值给state
  React.useEffect(() => {
    setState(count * 10)
  }, [count])
  return (
    <div>
      <div>count:{count}</div>
      <div>state:{state}</div>
      <button onClick={() => setcount(count + 1)}>点击count++</button>
    </div>
  )
}

举例三 

当我们在切换页面需要进行销毁操作的时候 比如清除定时器

import React from 'react'
export default function App() {
  const [count, setcount] = React.useState(1)
  const [state, setState] = React.useState(0)
  React.useEffect(() => {
    let T = setInterval(() => {
      setState(count => count * 10)
    }, 1000)
    return () => {
      clearInterval(T)
    }
  }, [count])
  return (
    <div>
      <div>count:{count}</div>
      <div>state:{state}</div>
      <button onClick={() => setcount(count + 1)}>点击count++</button>
    </div>
  )
}

三,useMemo

语法 

 React.useMemo(() => {
    return '必须有返回值'
  }, ['依赖项'])

useMemo的使用跟vue的计算属性很相似 缓存值 当依赖项发生改变的时候会重新计算

例子

import React from 'react'
export default function App() {
  const [count, setCount] = React.useState(1)
  const [state, setState] = React.useState(5)
  const total = () => {
    // total依赖的是count不是state
    console.log('total')
    return count + 10
  }
  return (
    <div>
      <p>count:{count}</p>
      <p>total:{total()}</p>
      <p>state:{state}</p>
      {/* 注意我们这里修改的是state,而不是count */}
      <button onClick={() => setState(state + 1)}>state++</button>
    </div>
  )
}

 上面的代码其实是定义了两个状态count state total是一个函数返回值为count的值加10也就是依赖count的值 因为在模板里调用了该函数 所以初始化会执行一次 当我们在点击修改state不是修改count 所以我们在修改state的时候 total这个函数不需要重新渲染 但是实际上当我们修改state 一样触发了total重新渲染

可以看到我们除了初始化调用了一次 我们点击按钮6次total触发了6次 

实际上我们只需要当count发生改变时候重新计算total的返回值 其他是不需要重新执行的

这时候我们就可以使用useMemo包裹

import React from 'react'
export default function App() {
  const [count, setCount] = React.useState(1)
  const [state, setState] = React.useState(5)
  const total = React.useMemo(() => {
    console.log('total')
    return count + 10
    // 依赖count
  }, [count])
  return (
    <div>
      <p>count:{count}</p>
      {/* 当使用usememo就是以值使用不需要加括号 */}
      <p>total:{total}</p>
      <p>state:{state}</p>
      {/* 注意我们这里修改的是state,而不是count */}
      <button onClick={() => setState(state + 1)}>state++</button>
    </div>
  )
}

这时候就变成了只有count改变才会重新计算

 

四,memo

memo是用来缓存组件的 

例子

import React from 'react'
export default function App() {
  // 定义一个状态
  const [count, setCount] = React.useState(1)
  console.log('父组件组件渲染')
  return (
    <div>
      <div>count:{count}</div>
      <button onClick={() => setCount(count + 1)}>count++</button>
      <Child />
    </div>
  )
}
// 子组件
const Child = () => {
  console.log('child子节点渲染')
  return <div>我是子组件</div>
}

当我们修改父组件状态会重新render 子组件也会跟着一起render

我们只需要使用memo包裹就能解决

// 子组件
const Child = React.memo(() => {
  console.log('child子节点渲染')
  return <div>我是子组件</div>
})

五,useCallback

语法

React.useCallback(() => {
    console.log('需要缓存的函数')
  }, ['依赖的值'])

例子

import React from 'react'
export default function App() {
  // 定义一个状态
  const [count, setCount] = React.useState(1)
  // 给子组件传递一个方法
  const childClick = () => {
    console.log('ChildClick')
  }
  console.log('父组件组件渲染')
  return (
    <div>
      <div>count:{count}</div>
      <button onClick={() => setCount(count + 1)}>count++</button>
      <Child fn={childClick} />
    </div>
  )
}
// 子组件
const Child = React.memo(({ fn }: { fn: () => void }) => {
  console.log('child子节点渲染')
  return <div onClick={fn}>我是子组件</div>
})

主要是定义一个子组件并且使用了memo包裹 但是当我们点击父组件更新状态时候 依旧会重新创建子组件

原因是因为我们传递了一个引用类型数据一个点击函数 当父组件发生变化时 函数会重新创建 当地址发生变化时候 子组件就会重新渲染

所以我们需要使用useCallback来缓存一下传递的方法这样就能避免子组件render

import React from 'react'
export default function App() {
  // 定义一个状态
  const [count, setCount] = React.useState(1)
  // 给子组件传递一个方法
  const childClick = React.useCallback(() => {
    console.log('ChildClick')
  }, [])
  console.log('父组件组件渲染')
  return (
    <div>
      <div>count:{count}</div>
      <button onClick={() => setCount(count + 1)}>count++</button>
      <Child fn={childClick} />
    </div>
  )
}
// 子组件
const Child = React.memo(({ fn }: { fn: () => void }) => {
  console.log('child子节点渲染')
  return <div onClick={fn}>我是子组件</div>
})

这样我们在点击父组件状态 因为函数已经被缓存所以不会重新创建 子组件就不会渲染 

所以useMemo是用来缓存值 useCallback是用来缓存函数 memo是用来缓存组件 需要相互配合使用节省性能

六,useRef

语法

跟vue中的ref差不多

const inputRef = React.useRef(null)

 例子

import React from 'react'

export default function App() {
  const inputRef = React.useRef(null)
  const btnFn = () => {
    console.log(inputRef.current)
  }
  return (
    <div>
      <input type='text' ref={inputRef} />
      <button onClick={btnFn}>按钮</button>
    </div>
  )
}

 点击按钮能拿到input结点

七, useTransition

语法

const [isPending, startTransition] = useTransition()

 返回一个状态值表示过渡任务的等待状态,以及一个启动该过渡任务的函数。

例子

import React from 'react'
export default function App() {
  const [isPendding, startTransition] = React.useTransition()
  const [search, setSearch] = React.useState('')
  const [list, setList] = React.useState<any>([])
  const searchFn = (e: any) => {
    setSearch(e.target.value)
    // 如果不设置 startTransition 会跟search同步渲染
    startTransition(() => {
      let arr = Array.from({ length: 200 }).fill(1)
      setList(arr)
    })
  }
  return (
    <div>
      <input type='text' onChange={e => searchFn(e)} />
      {isPendding ? (
        <div>loading...</div>
      ) : (
        list.map((item: any, index: any) => {
          return <div key={index}>{search}</div>
        })
      )}
    </div>
  )
}

八,useReducer

语法

const [state, dispatch] = useReducer(reducer, initialArg, init);

nitialArg为初始数据

useReducer返回一个数组,包含state,dispath

action为判断事件类型,通过dispatch传递

例子

import { useReducer } from 'react'
const App = () => {
  const [state, dispath] = useReducer(
    (state: any, action: any) => {
      // action{type,payload}
      switch (action.type) {
        case 'increment':
          return {
            count: state.count + 1
          }
        case 'decrement':
          return {
            count: state.count - 1
          }
        default:
          return state
      }
    },
    // 初始值
    {
      count: 0
    }
  )
  return (
    <div className='App'>
      <div>{state.count}</div>
      <button onClick={() => dispath({ type: 'increment' })}>increment</button>
      <button onClick={() => dispath({ type: 'decrement' })}>decrement</button>
    </div>
  )
}

export default App

九,useContext

用于跨组件传值

例子

// 引入createContext,useContext
import { useState, createContext, useContext } from 'react'

// createContext创建分享数据
const Context: any = createContext(null)
// 爷爷组件
export default function App() {
  const [n, setN] = useState(0)
  return (
    // 3.Context.Provider包裹需要传递参数的组件 value参数
    <div>
      <div>我是爷爷组件</div>
      <Context.Provider value={{ n, setN }}>
        <Child />
      </Context.Provider>
    </div>
  )
}

// 子组件
function Child() {
  // 通过useContext来获取数据
  const { n } = useContext(Context) as any
  return (
    <div>
      我是子组件 n: {n} <Child2 />
    </div>
  )
}

// 孙子组件
function Child2() {
  // 通过useContext来获取数据
  const { n, setN } = useContext(Context) as any
  const onClick = () => {
    setN((n: any) => n + 1)
  }
  return (
    <div>
      我是孙子组件:{n}
      <button onClick={onClick}>修改爷爷数据</button>
    </div>
  )
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值