「React进阶」 React全部api解读+基础实践大全(夯实基础2万字总结)(React Hooks)

简介: 一份不错的 React 学习指南

对于react-hooks,我已经写了三部曲,介绍了react-hooks使用,自定义hooks,以及react-hooks原理,感兴趣的同学可以去看看,文章末尾有链接,对于常用的api,我这里参考了react-hooks如何使用那篇文章。并做了相应精简化和一些内容的补充。

hooks.jpg

useState

useState可以弥补函数组件没有state的缺陷。useState可以接受一个初识值,也可以是一个函数actionaction返回值作为新的state。返回一个数组,第一个值为state读取值,第二个值为改变statedispatchAction函数。

我们看一个例子:

 const DemoState = (props) => {
    /* number为此时state读取值 ,setNumber为派发更新的函数 */
    let [number, setNumber] = useState(0) /* 0为初始值 */
    return (<div>
        <span>{ number }</span>
        <button onClick={ ()=> {
          setNumber(number+1) /* 写法一 */
          setNumber(number=>number + 1 ) /* 写法二 */
          console.log(number) /* 这里的number是不能够即时改变的  */
        } } >num++</button>
    </div>)
}

useEffect

useEffect可以弥补函数组件没有生命周期的缺点。我们可以在useEffect第一个参数回调函数中,做一些请求数据,事件监听等操作,第二个参数作为dep依赖项,当依赖项发生变化,重新执行第一个函数。

useEffect可以用作数据交互。

/* 模拟数据交互 */
function getUserInfo(a){
    return new Promise((resolve)=>{
        setTimeout(()=>{ 
           resolve({
               name:a,
               age:16,
           }) 
        },500)
    })
}
const DemoEffect = ({ a }) => {
    const [ userMessage , setUserMessage ] :any= useState({})
    const div= useRef()
    const [number, setNumber] = useState(0)
    /* 模拟事件监听处理函数 */
    const handleResize =()=>{}
    /* useEffect使用 ,这里如果不加限制 ,会是函数重复执行,陷入死循环*/
    useEffect(()=>{
        /* 请求数据 */
       getUserInfo(a).then(res=>{
           setUserMessage(res)
       })
       /* 操作dom  */
       console.log(div.current) /* div */
       /* 事件监听等 */
        window.addEventListener('resize', handleResize)
    /* 只有当props->a和state->number改变的时候 ,useEffect副作用函数重新执行 ,如果此时数组为空[],证明函数只有在初始化的时候执行一次相当于componentDidMount */
    },[ a ,number ])
    return (<div ref={div} >
        <span>{ userMessage.name }</span>
        <span>{ userMessage.age }</span>
        <div onClick={ ()=> setNumber(1) } >{ number }</div>
    </div>)
}

useEffect可以用作事件监听,还有一些基于dom的操作。,别忘了在useEffect第一个参数回调函数,返一个函数用于清除事件监听等操作。

const DemoEffect = ({ a }) => {
    /* 模拟事件监听处理函数 */
    const handleResize =()=>{}
    useEffect(()=>{
       /* 定时器 延时器等 */
       const timer = setInterval(()=>console.log(666),1000)
       /* 事件监听 */
       window.addEventListener('resize', handleResize)
       /* 此函数用于清除副作用 */
       return function(){
           clearInterval(timer) 
           window.removeEventListener('resize', handleResize)
       }
    },[ a ])
    return (<div  >
    </div>)
}

useMemo

useMemo接受两个参数,第一个参数是一个函数,返回值用于产生保存值。 第二个参数是一个数组,作为dep依赖项,数组里面的依赖项发生变化,重新执行第一个函数,产生新的值

应用场景:
1 缓存一些值,避免重新执行上下文

const number = useMemo(()=>{
   
   
    /** ....大量的逻辑运算 **/
   return number
},[ props.number ]) // 只有 props.number 改变的时候,重新计算number的值。

2 减少不必要的dom循环

/* 用 useMemo包裹的list可以限定当且仅当list改变的时候才更新此list,这样就可以避免selectList重新循环 */
 {
   
   useMemo(() => (
      <div>{
   
   
          selectList.map((i, v) => (
              <span
                  className={
   
   style.listSpan}
                  key={
   
   v} >
                  {
   
   i.patentName} 
              </span>
          ))}
      </div>
), [selectList])}

3 减少子组件渲染

/* 只有当props中,list列表改变的时候,子组件才渲染 */
const  goodListChild = useMemo(()=> <GoodList list={
   
    props.list } /> ,[ props.list ])

useCallback

useMemouseCallback 接收的参数都是一样,都是在其依赖项发生变化后才执行,都是返回缓存的值,区别在于 useMemo 返回的是函数运行的结果, useCallback 返回的是函数。 返回的callback可以作为props回调函数传递给子组件。

/* 用react.memo */
const DemoChildren = React.memo((props)=>{
   
   
   /* 只有初始化的时候打印了 子组件更新 */
    console.log('子组件更新')
   useEffect(()=>{
   
   
       props.getInfo('子组件')
   },[])
   return <div>子组件</div>
})
const DemoUseCallback=({
   
    id })=>{
   
   
    const [number, setNumber] = useState(1)
    /* 此时usecallback的第一参数 (sonName)=>{ console.log(sonName) }
     经过处理赋值给 getInfo */
    const getInfo  = useCallback((sonName)=>{
   
   
          console.log(sonName)
    },[id])
    return <div>
        {
   
   /* 点击按钮触发父组件更新 ,但是子组件没有更新 */}
        <button onClick={
   
    ()=>setNumber(number+1) } >增加</button>
        <DemoChildren getInfo={
   
   getInfo} />
    </div>
}

useRef

useRef的作用:

  • 一 是可以用来获取dom元素,或者class组件实例 。
  • react-hooks原理文章中讲过,创建useRef时候,会创建一个原始对象,只要函数组件不被销毁,原始对象就会一直存在,那么我们可以利用这个特性,来通过useRef保存一些数据。
const DemoUseRef = ()=>{
    const dom= useRef(null)
    const handerSubmit = ()=>{
        /*  <div >表单组件</div>  dom 节点 */
        console.log(dom.current)
    }
    return <div>
        {/* ref 标记当前dom节点 */}
        <div ref={dom} >表单组件</div>
        <button onClick={()=>handerSubmit()} >提交</button> 
    </div>
}

useLayoutEffect

useEffect执行顺序: 组件更新挂载完成 -> 浏览器 dom 绘制完成 -> 执行 useEffect 回调。
useLayoutEffect 执行顺序: 组件更新挂载完成 -> 执行 useLayoutEffect 回调-> 浏览器dom绘制完成。

所以说 useLayoutEffect 代码可能会阻塞浏览器的绘制 。我们写的 effectuseLayoutEffectreact在底层会被分别打上PassiveEffectHookLayout,在commit阶段区分出,在什么时机执行。

const DemoUseLayoutEffect = () => {
    const target = useRef()
    useLayoutEffect(() => {
        /*我们需要在dom绘制之前,移动dom到制定位置*/
        const { x ,y } = getPositon() /* 获取要移动的 x,y坐标 */
        animate(target.current,{ x,y })
    }, []);
    return (
        <div >
            <span ref={ target } className="animate"></span>
        </div>
    )
}

useReducer

react-hooks原理那篇文章中讲解到,useState底层就是一个简单版的useReducer

useReducer 接受的第一个参数是一个函数,我们可以认为它就是一个 reducer , reducer 的参数就是常规 reducer 里面的 stateaction ,返回改变后的 state , useReducer 第二个参数为 state 的初始值 返回一个数组,数组的第一项就是更新之后 state 的值 ,第二个参数是派发更新的 dispatch 函数。

我们来看一下useReducer如何使用:

const DemoUseReducer = ()=>{
   
   
    /* number为更新后的state值,  dispatchNumbner 为当前的派发函数 */
   const [ number , dispatchNumbner ] = useReducer((state,action)=>{
   
   
       const {
   
    payload , name  } = action
       /* return的值为新的state */
       switch(name){
   
   
           case 'add':
               return state + 1
           case 'sub':
               return state - 1 
           case 'reset':
             return payload       
       }
       return state
   },0)
   return <div>
      当前值:{
   
    number }
      {
   
    /* 派发更新 */ }
      <button onClick={
   
   ()=>dispatchNumbner({
   
    name:'add' })} >增加</button>
      <button onClick={
   
   ()=>dispatchNumbner({
   
    name:'sub' })} >减少</button>
      <button onClick={
   
   ()=>dispatchNumbner({
   
    name:'reset' ,payload:666 })} >赋值</button>
      {
   
    /* 把dispatch 和 state 传递给子组件  */ }
      <MyChildren  dispatch={
   
    dispatchNumbner } State={
   
   {
   
    number }} />
   </div>
}

useContext

我们可以使用 useContext ,来获取父级组件传递过来的 context 值,这个当前值就是最近的父级组件 Provider 设置的 value 值,useContext 参数一般是由 createContext 方式引入 ,也可以父级上下文 context 传递 ( 参数为 context )。useContext 可以代替 context.Consumer 来获取 Provider 中保存的 value

/* 用useContext方式 */
const DemoContext = ()=> {
    const value:any = useContext(Context)
    /* my name is alien */
return <div> my name is { value.name }</div>
}
/* 用Context.Consumer 方式 */
const DemoContext1 = ()=>{
    return <Context.Consumer>
         {/*  my name is alien  */}
        { (value)=> <div> my name is { value.name }</div> }
    </Context.Consumer>
}

export default ()=>{
    return <div>
        <Context.Provider value={
  
  { name:'alien' , age:18 }} >
            <DemoContext />
            <DemoContext1 />
        </Context.Provider>
    </div>
}

useImperativeHandle

useImperativeHandle 可以配合 forwardRef自定义暴露给父组件的实例值。这个很有用,我们知道,对于子组件,如果是class类组件,我们可以通过ref获取类组件的实例,但是在子组件是函数组件的情况,如果我们不能直接通过ref的,那么此时useImperativeHandleforwardRef配合就能达到效果。

useImperativeHandle接受三个参数:

  • 第一个参数ref: 接受 forWardRef 传递过来的 ref

  • 第二个参数 createHandle :处理函数,返回值作为暴露给父组件的ref对象。

  • 第三个参数 deps:依赖项 deps,依赖项更改形成新的ref对象。

我们来模拟给场景,用useImperativeHandle,使得父组件能让子组件中的input自动赋值并聚焦。

function Son (props,ref) {
   
   
    console.log(props)
    const inputRef = useRef(null)
    const [ inputValue , setInputValue ] = useState('')
    useImperativeHandle(ref,()=>{
   
   
       const handleRefs = {
   
   
           /* 声明方法用于聚焦input框 */
           onFocus(){
   
   
              inputRef.current.focus()
           },
           /* 声明方法用于改变input的值 */
           onChangeValue(value){
   
   
               setInputValue(value)
           }
       }
       return handleRefs
    },[])
    return <div>
        <input
            placeholder="请输入内容"
            ref={
   
   inputRef}
            value={
   
   inputValue}
        />
    </div>
}

const ForwarSon = forwardRef(Son)

class Index extends React.Component{
   
   
    inputRef = null
    handerClick(){
   
   
       const {
   
    onFocus , onChangeValue } =this.cur
       onFocus()
       onChangeValue('let us learn React!')
    }
    render(){
   
   
        return <div style={
   
   {
   
    marginTop:'50px' }} >
            <ForwarSon ref={
   
   node => (this.inputRef = node)} />
            <button onClick={
   
   this.handerClick.bind(this)} >操控子组件</button>
        </div>
    }
}

效果:

useImperativeHandle.gif

useDebugValue

useDebugValue 可用于在 React 开发者工具中显示自定义 hook 的标签。这个hooks目的就是检查自定义hooks

function useFriendStatus(friendID) {
   
   
  const [isOnline, setIsOnline] = useState(null);
  // ...
  // 在开发者工具中的这个 Hook 旁边显示标签
  // e.g. "FriendStatus: Online"
  useDebugValue(isOnline ? 'Online' : 'Offline');

  return isOnline;
}

我们不推荐你向每个自定义 Hook 添加 debug 值。当它作为共享库的一部分时才最有价值。在某些情况下,格式化值的显示可能是一项开销很大的操作。除非需要检查 Hook,否则没有必要这么做。因此,useDebugValue 接受一个格式化函数作为可选的第二个参数。该函数只有在 Hook 被检查时才会被调用。它接受 debug 值作为参数,并且会返回一个格式化的显示值。

useTransition

useTransition允许延时由state改变而带来的视图渲染。避免不必要的渲染。它还允许组件将速度较慢的数据获取更新推迟到随后渲染,以便能够立即渲染更重要的更新。

const TIMEOUT_MS = {
   
    timeoutMs: 2000 }
const [startTransition, isPending] = useTransition(TIMEOUT_MS)
  • useTransition 接受一个对象, timeoutMs代码需要延时的时间。

  • 返回一个数组。第一个参数: 是一个接受回调的函数。我们用它来告诉 React 需要推迟的 state第二个参数: 一个布尔值。表示是否正在等待,过度状态的完成(延时state的更新)。

下面我们引入官网的列子,来了解useTransition的使用。

const SUSPENSE_CONFIG = {
   
    timeoutMs: 2000 };

function App() {
   
   
  const [resource, setResource] = useState(initialResource);
  const [startTransition, isPending] = useTransition(SUSPENSE_CONFIG);
  return (
    <>
      <button
        disabled={
   
   isPending}
        onClick={
   
   () => {
   
   
          startTransition(() => {
   
   
            const nextUserId = getNextId(resource.userId);
            setResource(fetchProfileData(nextUserId));
          });
        }}
      >
        Next
      </button>
      {
   
   isPending ? " 加载中..." : null}
      <Suspense fallback={
   
   <Spinner />}>
        <ProfilePage resource={
   
   resource} />
      </Suspense>
    </>
  );
}

在这段代码中,我们使用 startTransition 包装了我们的数据获取。这使我们可以立即开始获取用户资料的数据,同时推迟下一个用户资料页面以及其关联的 Spinner 的渲染 2 秒钟( timeoutMs 中显示的时间)。

这个api目前处于实验阶段,没有被完全开放出来。

相关文章
|
2月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
223 2
|
20天前
|
XML JSON 缓存
深入理解RESTful API设计原则与实践
在现代软件开发中,构建高效、可扩展的应用程序接口(API)是至关重要的。本文旨在探讨RESTful API的核心设计理念,包括其基于HTTP协议的特性,以及如何在实际应用中遵循这些原则来优化API设计。我们将通过具体示例和最佳实践,展示如何创建易于理解、维护且性能优良的RESTful服务,从而提升前后端分离架构下的开发效率和用户体验。
|
15天前
|
机器学习/深度学习 搜索推荐 API
淘宝/天猫按图搜索(拍立淘)API的深度解析与应用实践
在数字化时代,电商行业迅速发展,个性化、便捷性和高效性成为消费者新需求。淘宝/天猫推出的拍立淘API,利用图像识别技术,提供精准的购物搜索体验。本文深入探讨其原理、优势、应用场景及实现方法,助力电商技术和用户体验提升。
|
28天前
|
前端开发 JavaScript API
探究 React Hooks:如何利用全新 API 优化组件逻辑复用与状态管理
本文深入探讨React Hooks的使用方法,通过全新API优化组件逻辑复用和状态管理,提升开发效率和代码可维护性。
|
28天前
|
缓存 API 开发者
构建高效后端服务:RESTful API设计原则与实践
【10月更文挑战第43天】在数字化时代的浪潮中,后端服务的稳定性和效率成为企业竞争力的关键。本文将深入探讨如何构建高效的后端服务,重点介绍RESTful API的设计原则和实践技巧,帮助开发者提升服务的可用性、可扩展性和安全性。通过实际代码示例,我们将展示如何将这些原则应用到日常开发工作中,以确保后端服务能够支撑起现代Web和移动应用的需求。
|
28天前
|
存储 JSON 测试技术
构建高效后端API:实践和原则
【10月更文挑战第43天】本文深入探讨了如何设计和实现高效、可维护的后端API,强调了设计哲学、最佳实践和常见陷阱。通过具体示例,我们展示了如何运用这些原则来提高API的性能和可用性。
|
1月前
|
Prometheus 监控 Java
深入探索:自制Agent监控API接口耗时实践
在微服务架构中,监控API接口的调用耗时对于性能优化至关重要。通过监控接口耗时,我们可以识别性能瓶颈,优化服务响应速度。本文将分享如何自己动手实现一个Agent来统计API接口的调用耗时,提供一种实用的技术解决方案。
43 3
|
14天前
|
监控 搜索推荐 测试技术
电商API的测试与用途:深度解析与实践
在电子商务蓬勃发展的今天,电商API成为连接电商平台、商家、消费者和第三方开发者的重要桥梁。本文深入探讨了电商API的核心功能,包括订单管理、商品管理、用户管理、支付管理和物流管理,并介绍了有效的测试技巧,如理解API文档、设计测试用例、搭建测试环境、自动化测试、压力测试、安全性测试等。文章还详细阐述了电商API的多样化用途,如商品信息获取、订单管理自动化、用户数据管理、库存同步、物流跟踪、支付处理、促销活动管理、评价管理、数据报告和分析、扩展平台功能及跨境电商等,旨在为开发者和电商平台提供有益的参考。
23 0
|
1月前
|
监控 安全 应用服务中间件
微服务架构下的API网关设计策略与实践####
本文深入探讨了在微服务架构下,API网关作为系统统一入口点的设计策略、实现细节及其在实际应用中的最佳实践。不同于传统的摘要概述,本部分将直接以一段精简的代码示例作为引子,展示一个基于NGINX的简单API网关配置片段,随后引出文章的核心内容,旨在通过具体实例激发读者兴趣,快速理解API网关在微服务架构中的关键作用及实现方式。 ```nginx server { listen 80; server_name api.example.com; location / { proxy_pass http://backend_service:5000;
|
1月前
|
XML API 网络架构
深入理解RESTful API设计原则与实践
【10月更文挑战第26天】在数字化浪潮中,API(应用程序编程接口)成为连接不同软件组件的桥梁。本文将深入浅出地探讨如何根据REST(Representational State Transfer)原则设计高效、易于维护和扩展的API,同时分享一些实用的代码示例,帮助开发者构建更加健壮和用户友好的服务。