1. 简介

在 React 的世界中,有容器组件和 UI 组件之分,在 React Hooks 出现之前,UI 组件我们可以使用函数组件,无状态组件来展示 UI,而对于容器组件,函数组件就显得无能为力,我们依赖于类组件来获取数据,处理数据,并向下传递参数给 UI 组件进行渲染。React在v16.8 的版本中推出了 React Hooks 新特性,Hook是一套工具函数的集合,它增强了函数组件的功能,hook不等于函数组件,所有的hook函数都是以use开头。

使用 React Hooks 相比于从前的类组件有以下几点好处:

  • 代码可读性更强,原本同一块功能的代码逻辑被拆分在了不同的生命周期函数中,容易使开发者修改代码时不易去查找,通过 React Hooks 可以将功能代码聚合,方便维护
  • 组件树层级变浅,在原本的代码中,我们经常使用 HOC/render/Props 等方式来复用组件的状态,增强功能等,无疑增加了组件树层数及渲染,而在 React Hooks 中,这些功能都可以通过强大的自定义的 Hooks 来实现

使用hook限制:

  • hook 只能用在函数组件中,class 组件不行
  • 普通函数不能使用 hook,默认只能是函数组件才能用

例外:普通函数名称以 use 开头也可以,(自定义的函数以 use 开头,称为自定义 hook)

  • 函数组件内部的函数也不能使用 hook
  • hook 函数一定要放在函数组件的第一层,别放在 if/for 中(块级作用域)
  • 要求函数组件名称必须首字母大写

2. useState使用

概述:

类组件中有一个状态属性,可以通过此特殊属性完成私有数据的操作。操作此 state 数据可以触发视图更新(this.setState())。

函数组件中,从 react16.8 之后,提供一个 hook 函数 useState 方法,它可以模拟出类组件中的状态。

语法:

let [变量,函数] = useState(值|()=>值)

变量就可以得到useState中的值,函数就可以修改值。值的存储使用了闭包。

使用:

import React, { useState } from 'react';
const App = () => {
  // 相当于在App函数组件是定义一个state数据,变量名为 count,修改此count的值的方法为setCount
  // 写法1:值
  // let [count, setCount] = useState(100)
  // 有的时候,在项目中的初始数据,要经过一系列的运算才能出来的初始值,这时候就可以使用函数的写法
  // 写法2:函数
  let [count, setCount] = useState(() => 100)
  const addCount = () => {
    setCount(count   1)
  }
  return (
    <div>
      <h1>{count}</h1>
      <button onClick={addCount}>    </button>
    </div>
  );
}
export default App;

处理并发操作:

import React, { useState } from 'react';
const App = () => {
  let [count, setCount] = useState(() => 100)
  const addCount = () => {
    // 并发处理数据的完整性得不到保证
    // setCount(count   1)  // 100 1
    // setCount(count   1)  // 100 1
    // setCount(count   1)  // 100 1
    // 并发处理 -- 推荐写法,这样写数据的完整性可靠的
    setCount(v => v   1)
    setCount(v => v   1)
    setCount(v => v   1)
  }
  return (
    <div>
      <h1>{count}</h1>
      <button onClick={addCount}>    </button>
    </div>
  );
}
export default App;

使用useState完成表单项自定义hook函数:

如果我们有两个 input 框需要变为受控组件,我们可以这样写:

import React, { useState } from 'react';
const App = () => {
  let [username, setUsername] = useState('')
  let [password, setPassword] = useState('')
  return (
    <div>
      {/* 受控组件 */}
      <input type="text" value={username} onChange={e => setUsername(e.target.value)} />
      <input type="text" value={password} onChange={e => setPassword(e.target.value)} />
    </div>
  );
}
export default App;

上面的做法让 return 部分的代码太过复杂,我们可以使用自定义 hook 函数来简化这部分的代码:

import React, { useState } from 'react';
// 在react中,定义的函数是以use开头,则认为它就是一个自定义hook函数
// 在自定义hook函数中,可以调用内置hook
function useInput(initialValue = '') {
  let [value, setValue] = useState(initialValue)
  return { value, onChange: e => setValue(e.target.value.trim()) }
}
const App = () => {
  let usernameInput = useInput('')
  let passwordInput = useInput('')
  return (
    <div>
      {/* 受控组件 */}
      <input type="text" {...usernameInput} />
      <input type="text" {...passwordInput} />
    </div>
  );
}
export default App;

我们还可以使用模块化的思想,将自定义 hook 函数拆分到另一个文件中:

useInput.js:

import { useState } from 'react';
// 在react中,定义的函数是以use开头,则认为它就是一个自定义hook函数
// 在自定义hook函数中,可以调用内置hook
const useInput = (initialValue = '') => {
  let [value, setValue] = useState(initialValue)
  return { value, onChange: e => setValue(e.target.value.trim()) }
}
export default useInput

App.jsx:

import React from 'react';
import useInput from './hook/useInput';
const App = () => {
  let usernameInput = useInput('')
  let passwordInput = useInput('')
  return (
    <div>
      <input type="text" {...usernameInput} />
      <input type="text" {...passwordInput} />
    </div>
  );
}
export default App;

3. useEffect使用

概述:

此 hook 可以模拟函数组件的生命周期,函数组件对于在一些生命周期中操作还是无能为力,所以 React 提供了 useEffect 来帮助开发者处理函数组件,来帮助模拟完成一部份的开发中非常常用的生命周期方法。常被别的称为:副作用处理函数。此函数的操作是异步的。

它并不能模拟全部的钩子函数,它只能模拟下面这几个:componentDidMount、componentDidUpdate、componentWillUnmout。

注意:useEffect中不能有返回值,React它要自动回收

比如说下面这种场景,我们希望 console.log 函数中的内容只打印一次,但是每当试图更新的时候,console.log 都会重新执行:

import React, { useState } from 'react';
const App = () => {
  let [count, setCount] = useState(100)
  const addCount = () => setCount(count   1)
  console.log('App -- 要求此处只打印一次');
  return (
    <div>
      <h3>App组件 --- {count}</h3>
      <button onClick={addCount}>    </button>
    </div>
  );
}
export default App;

这时候我们就可以使用 useEffect 函数来实现上述需求。

使用:

模拟:componentDidMount componentDidUpdate

import React, { useState, useEffect } from 'react';
const App = () => {
  let [count, setCount] = useState(100)
  const addCount = () => setCount(count   1)
  // 模拟:componentDidMount componentDidUpdate(可以调用多次)
  useEffect(() => {
    console.log('App -- useEffect');
  })
  return (
    <div>
      <h3>App组件 --- {count}</h3>
      <button onClick={addCount}>    </button>
    </div>
  );
}
export default App;

模拟:componentDidMount(这种写法可以模拟网络请求)

import React, { useState, useEffect } from 'react';
const App = () => {
  let [count, setCount] = useState(100)
  const addCount = () => setCount(count   1)
  // 模拟:componentDidMount(可以调用多次)
  // 参数2:依赖项,如果为空数据,则只执行1次
  // 一般在这样的写法中,完成网络请求
  useEffect(() => {
    console.log('App -- useEffect');
  }, [])
  return (
    <div>
      <h3>App组件 --- {count}</h3>
      <button onClick={addCount}>    </button>
    </div>
  );
}
export default App;

利用依赖项,模拟componentDidMount、componentDidUpdate

import React, { useState, useEffect } from 'react';
const App = () => {
  let [count, setCount] = useState(100)
  let [name, setName] = useState('')
  const addCount = () => setCount(count   1)
  // 下面这种写法,也可以实现相同的功能
  // let [count, setCount] = useState({ num: 100 })
  // const addCount = () => setCount({ num: count.num   1 })
  // const addCount = () => setCount(v => ({ num: v.num   1 }))
  // 参数2中依赖项,进行填值,只要依赖项中的值,发生改变,则进行触发
  // componentDidMount componentDidUpdate
  useEffect(() => {
    console.log('App -- useEffect');
  // 这里的依赖项中只填了count,所以只有count发生改变,才会触发当前函数
  }, [count])
  // 对依赖项的使用,可以像下面这样分开写,也可以写在同一个数组中
  // useEffect(() => {
  //   console.log('App -- useEffect');
  // }, [name]) 
  return (
    <div>
      <input value={name} onChange={e => setName(e.target.value)} />
      <h3>App组件 --- {count}</h3>
      <button onClick={addCount}>    </button>
    </div>
  );
}
export default App;

有依赖项,模拟:componentDidMount、componentDidUpdate、componentWillUnmout

import React, { useState, useEffect } from 'react';
const App = () => {
  let [count, setCount] = useState(100)
  let [name, setName] = useState('')
  const addCount = () => setCount(count   1)
  // 有依赖项,只要count改变,则触发
  // componentDidMount componentDidUpdate componentWillUnmout
  useEffect(() => {
    console.log('App -- useEffect');
    // 返回回调函数中就是 componetWillUnMount
	// 在执行下一个 effect 之前,上一个 effect 就已被清除
    return () => {
      console.log('componentWillUnmout');
    }
  }, [count])
  return (
    <div>
      <input value={name} onChange={e => setName(e.target.value)} />
      <h3>App组件 --- {count}</h3>
      <button onClick={addCount}>    </button>
    </div>
  );
}
export default App;

模拟组件销毁

import React, { useState, useEffect } from 'react';
const App = () => {
  let [count, setCount] = useState(100)
  let [name, setName] = useState('')
  const addCount = () => setCount(count   1)
  return (
    <div>
      <input value={name} onChange={e => setName(e.target.value)} />
      <h3>App组件 --- {count}</h3>
      {
        count > 103 ? null : <Child />
      }
      <button onClick={addCount}>    </button>
    </div>
  );
}
function Child() {
  // componentDidMount componentWillUnmout  -- 一般模拟组件的销毁
  useEffect(() => {
    console.log('child -- componentDidMount');
    return () => {
      console.log('child -- componentWillUnmout');
    }
  }, [])
  return (
    <div>
      <h3>Child</h3>
    </div>
  )
}
export default App;

useEffect发起网络请求

import React, { useState, useEffect } from 'react';
import { get } from '@/utils/http'
const App = () => {
  let [films, setFilms] = useState([])
  // useEffect 
  // 在之前的版本:第1个参数要求只能是一个普通的函数,没有返回对象,可以返回回调函数,回调函数它模拟生命周期componentWillUnmout
  // 还有最后一个问题。在代码中,我们使用async / await从第三方API获取数据。如果你对async/await熟悉的话,你会知道,每个async函数都会默认返回一个隐式的promise。但是,useEffect不应该返回任何内容。这就是为什么会在控制台日志中看到以下警告:
  // Warning: useEffect function must return a cleanup function or nothing. Promises and useEffect(async () => …) are not supported, but you can call an async function inside an effect
  useEffect(async () => {
    let ret = await get('/api/swiper')
    setFilms(ret.data)
  }, [])
  // 如果useEffect顶层不支持 async/await可以使用如下的解决方案
  /* useEffect(() => {
    ; (async function () {
      let ret = await get('/api/swiper')
      setFilms(ret.data)
    })()
  }, []) */
  return (
    <div>
      <h3>App组件</h3>
      <ul>
        {
          films.map(item => (
            <li key={item.id} > {item.title}</li>
          ))
        }
      </ul>
    </div >
  );
}
export default App;

useEffect 封装网络请求:

useSwiper.js:

import { useEffect, useState } from 'react';
import { get } from '@/utils/http'
const useSwiper = (initialValue = []) => {
  let [data, setData] = useState(initialValue)
  useEffect(async () => {
    let ret = await get('/api/swiper')
    setData(ret.data)
  }, [])
  return data
}
export default useSwiper

App.jsx:

import useSwiper from "./hook/useSwiper";
const App = () => {
  let data = useSwiper()
  return (
    <div>
      <h3>App组件</h3>
      <ul>
        {
          data.map(item => (
            <li key={item.id} > {item.title}</li>
          ))
        }
      </ul>
    </div >
  );
}
export default App;

封装网络请求时,实现分页:

useGoodFood.js:

import { useEffect, useState } from 'react';
import { get } from '@/utils/http'
const useGoodFood = (initialValue = []) => {
  let [data, setData] = useState(initialValue)
  let [page, setPage] = useState(1)
  const loadData = async () => {
    let ret = await get('/api/goodfood?page='   page)
    if (ret.data.length > 0) {
      // setData(v => [...v, ...ret.data])
      setData(ret.data)
      setPage(v => v   1)
    }
  }
  useEffect(() => {
    loadData()
  }, [])
  return [data, loadData]
}
export default useGoodFood

App.jsx:

import useGoodFood from "./hook/useGoodFood";
const App = () => {
  let [data, loadData] = useGoodFood()
  return (
    <div>
      <h3>App组件</h3>
      <ul>
        {
          data.map(item => (
            <li key={item.id} > {item.name}</li>
          ))
        }
      </ul>
      <hr />
      <button onClick={() => {
        loadData()
      }}>下一页</button>
    </div >
  );
}
export default App;

到此这篇关于React中hook函数与useState及useEffect的使用的文章就介绍到这了,更多相关React中hook函数内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

React中hook函数与useState及useEffect的使用的更多相关文章

  1. ios – React native链接到另一个应用程序

    如果是错误的,有人知道如何调用正确的吗?

  2. ios – React Native – 在异步操作后导航

    我正在使用ReactNative和Redux开发移动应用程序,我正面临着软件设计问题.我想调用RESTAPI进行登录,如果该操作成功,则导航到主视图.我正在使用redux和thunk所以我已经实现了异步操作,所以我的主要疑问是:我应该把逻辑导航到主视图?我可以直接从动作访问导航器对象并在那里执行导航吗?.我对组件中的逻辑没有信心.似乎不是一个好习惯.有没有其他方法可以做到这一点?

  3. 在ios中使用带有React Native(0.43.4)的cocoapods的正确方法是什么?

    我已经挖掘了很多帖子试图使用cocoapods为本地ios库设置一个反应原生项目,但我不可避免地在#import中找到了丢失文件的错误.我的AppDelegate.m文件中的语句.什么是使用反应原生的可可豆荚的正确方法?在这篇文章发表时,我目前的RN版本是0.43.4,而我正在使用Xcode8.2.1.这是我的过程,好奇我可能会出错:1)

  4. ios – React Native WebView滚动行为无法按预期工作

    如何确保滚动事件的行为与ReactNative应用程序中的浏览器相同?

  5. ios – React Native – BVLinearGradient – 找不到’React/RCTViewManager.h’文件

    谢谢.解决方法几天前我遇到了完全相同的问题.问题是在构建应用程序时React尚未链接.试试这个:转到Product=>Scheme=>管理方案…=>点击你的应用程序Scheme,然后点击Edit=>转到Build选项卡=>取消选中ParallelizeBuild然后点击标志添加目标=>搜索React,选择第一个名为React的目标,然后单击Add然后在目标列表中选择React并将其向上拖动到该列表中的第一个.然后转到Product=>再次清理并构建项目.这应该有所帮助.

  6. ios – React Native – NSNumber无法转换为NSString

    解决方法在你的fontWeight()函数中也许变成:

  7. ios – React native error – react-native-xcode.sh:line 45:react-native:command not found命令/ bin/sh失败,退出代码127

    尝试构建任何(新的或旧的)项目时出现此错误.我的节点是版本4.2.1,react-native是版本0.1.7.我看过其他有相同问题的人,所以我已经更新了本机的最新版本,但是我仍然无法通过xcode构建任何项目.解决方法要解决此问题,请使用以下步骤:>使用节点版本v4.2.1>cd进入[你的应用]/node_modules/react-native/packager>$sh./packager.s

  8. 反应原生 – 如何通过Xcode构建React Native iOS应用程序到设备?

    我试图将AwesomeProject应用程序构建到设备上.构建成功并启动屏幕显示,但后来我看到一个红色的“无法连接到开发服务器”屏幕.它表示“确保节点服务器正在运行–从Reactroot运行”npmstart“.看起来节点服务器已经运行,因为当我做npm启动时,我收到一个EADDRINUSE消息,表示该端口已经在使用.解决方法从设备访问开发服务器您可以使用开发服务器快速迭代设备.要做到这一点,你的

  9. 静音iOS推送通知与React Native应用程序在后台

    我有一个ReactNative应用程序,我试图获得一个发送到JavaScript处理程序的静默iOS推送通知.我看到的行为是AppDelegate中的didReceiveRemoteNotification函数被调用,但是我的JavaScript中的处理程序不会被调用,除非应用程序在前台,或者最近才被关闭.我很困惑的事情显然是应用程序正在被唤醒,并且它的didReceiveRemoteNotifi

  10. 如何为iOS的React Native设置分析

    所以我已经完成了一个针对iOS的ReactNative项目,但是我想在其中分析.我尝试了react-native-google-analytics软件包,但是问题阻止了它的正常工作.此外,react-native-cordova-plugin软件包只适用于Android,因此插入Cordova插件进行分析的能力现在已成为问题.我也没有Swift/ObjectiveC的经验,所以将完全失去GA的插入.有没有人有任何建议如何连接GoogleAnalytics的ReactNativeforiOS?

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部