从hr口中了解react的状态管理库(mobx, recoil), 立马过来学习之recoil

简介: 从hr口中了解react的状态管理库(mobx, recoil), 立马过来学习之recoil

Recoil


RecoilRoot


一个组件。如果我们想要在项目中每个组件中都是用recoil管理的数据,我们就需要使用它包裹整个项目的根组件。


atom


定义共享数据的state


import { atom, useRecoilState } from 'recoil'
    import React from 'react'
    const counter = atom({
      key: 'myCounter',
      // 定义默认值
      default: 0
    })
    export default function Test4() {
      // 对atom提供的state进行读写。
      const [count, setCount] = useRecoilState(counter)
      const incrementByOne = () => setCount(count + 1)
      return (
        <div>
          Count: {count}
          <br />
          <button onClick={incrementByOne}>Increment</button>
        </div>
      )
    }


selector


是一个纯函数。如果只提供get方法,那么他将是只读的,如果还提供了set方法,那么它是可读写的,返回RecoilState类型的值。


  • get方法,返回一个修饰后的state。他接受一个对象作为参数,其中为get, getCallback。


  • set方法, 修改state中定义的属性。他的参数一为一个对象,其中有get, set, reset。参数二是修改传入的值。


const proxySelector = selector({
      key: 'ProxySelector',
      // 直接传入想要获取的auto,而不是auto中定义的key值字符串
      get: ({ get }) => get(counter),
      set: ({ set }, newValue) => set(counter, newValue)
    })
    export default function Test4() {
      // 对selector提供的state进行读写。
      const [count, setCount] = useRecoilState(proxySelector)
      const incrementByOne = () => setCount(count + 1)
      return (
        <div>
          Count: {count}
          <br />
          <button onClick={incrementByOne}>Increment</button>
        </div>
      )
    }


useRecoilState


返回一个数组,第一个元素是 state 的值,第二个元素是一个 setter 函数,调用该函数时会更新为给定 state 的值。


这个api和useState hook十分相似。


  • 该方法的setter函数如果传递一个值,那么将覆盖原来的值。


  • 如果传入一个函数,该函数接收原来的值作为参数,然后返回新值。注意:不管是哪种修改,都是将原值覆盖。


useRecoilValue


返回给定 Recoil state 的值。如果指向读取state的值,那么就应该使用这个hook。


useSetRecoilState


返回一个 setter 函数,用来更新可写 Recoil state 的值。如果你只想修改state,那么就应该使用这个hook。


可以传给此 setter 函数一个新的值,也可以传入一个更新函数,此函数接受上一次的值作为其参数。我们来测试一下,当我们只想要修改state属性时,使用useRecoilState和useSetRecoilState的区别:


// 使用useRecoilState
    import React from 'react'
    import { useRecoilState } from 'recoil'
    import { personState } from '../../recoilStore/index'
    export default function UseStateTest() {
      const [person, setPerson] = useRecoilState(personState)
      const handleClick = () => {
        setPerson(100)
      }
      console.log('=====看其页面是否重新渲染====引用state,但是未使用')
      return (
        <div>
          <div>使用useRecoilState</div>
          <button onClick={handleClick}>修改</button>
        </div>
      )
    }


// 使用useSetRecoilState
    import React from 'react'
    import { useSetRecoilState } from 'recoil'
    import { personState } from '../../recoilStore/index'
    export default function NotUseStateTest() {
      const setPerson = useSetRecoilState(personState)
      console.log('=====看其页面是否重新渲染====不引用state')
      return (
        <div>
          <button
            onClick={() => {
              setPerson(2)
            }}
          >
            修改
          </button>
        </div>
      )
    }


网络异常,图片无法展示
|


如果不需要使用state,而只是想要修改state,就应该使用useSetRecoilState,而不是useRecoilState。


useResetRecoilState


返回一个函数,用来把给定 state 重置为其初始值。函数中不需要传递任何值。


import {todoListState} from "../atoms/todoListState";
    const TodoResetButton = () => {
      const resetList = useResetRecoilState(todoListState);
      return <button onClick={resetList}>Reset</button>;
    };


useRecoilStateLoadable


此钩子可用于读取异步 selector 的值。为获取到指定状态值,此钩子将隐含地订阅对应组件。


const userInfo = atom({
      key: 'userInfo',
      default: {}
    })
    const userInfoSelector = selector({
      key: 'userInfoSelector',
      get: async () => {
        const result = await axios('http://myjson.dit.upm.es/api/bins/fo6v')
        return result.data
      },
      set: ({ set }, newValue) => set(userInfo, newValue)
    })
    function AsyncSelector() {
    // 返回一个loadable和一个更新state的函数组成的元组
      const [userInfoLoadable, setUserInfo] =
        useRecoilStateLoadable(userInfoSelector)
       // 当返回正确的值后,修改state
      if (userInfoLoadable.state === 'hasValue') {
        setUserInfo(userInfoLoadable.contents)
      }
      const userInfos = useRecoilValue(userInfo)
      return (
        <div>
          <p>异步</p>
          <Suspense fallback={<div>loading..</div>}>
            <p>{userInfos.name}</p>
            <p>{userInfos.age}</p>
          </Suspense>
        </div>
      )
    }


useRecoilValueLoadable


此 hook 用来读取异步 selector 的值。使用此 hook 会使组件隐式地订阅给定的 state。

它会返回一个 Loadable 对象。


import React, { Suspense, useEffect } from 'react'
    import { atom, selector, useRecoilValue, useRecoilValueLoadable } from 'recoil'
    import axios from 'axios'
    const userInfoSelector = selector({
      key: 'userInfoSelector',
      get: async () => {
        const result = await axios('http://myjson.dit.upm.es/api/bins/fo6v')
        return result.data
      }
    })
    function AsyncSelector() {
      const userInfoLoadable = useRecoilValueLoadable(userInfoSelector)
      console.log('======', userInfoLoadable)
      return (
        <div>
          <p>异步</p>
          <Suspense fallback={<div>loading..</div>}>
            {userInfoLoadable.state === 'hasValue' ? (
              <>
                <p>{userInfoLoadable.contents.name}</p>
                <p>{userInfoLoadable.contents.age}</p>
              </>
            ) : null}
          </Suspense>
        </div>
      )
    }
    export default AsyncSelector


未出现错误


网络异常,图片无法展示
|


出现错误


网络异常,图片无法展示
|


atomFamily


默认情况下,我们不能给atom中的default传递参数,来指定我们想要使用的默认值。如果让外界传入,我们就可以使用atomFamily来做到。其实我个人感觉这个api没啥用。


const myAtom = atom({
      key: 'myAtom',
      default: (params) => params
    })
    function Test10() {
      const myA = useRecoilValue(myAtom({ name: 'zh' }))
      return (
        <div>
          <h2>测试默认情况下是否可以给atom函数传递参数</h2>
          <p>{myA.name}</p>
        </div>
      )
    }


网络异常,图片无法展示
|


正确的方式


// const myAtom = atom({
    //   key: 'myAtom',
    //   default: (params) => params
    // })
    const myAtom = atomFamily({
      key: 'myAtom',
      default: (params) => params
    })
    function Test10() {
      const myA = useRecoilValue(myAtom({ name: 'zh' }))
      return (
        <div>
          <h2>测试默认情况下是否可以给atom函数传递参数</h2>
          <p>{myA.name}</p>
        </div>
      )
    }


网络异常,图片无法展示
|


selectorFamily


默认情况下,我们不能在使用selector的情况下给getter,setter方法传递参数,如果想要传递参数,我们就需要使用这个api。其他内容都和selector一样。


const myAtom = atom({
      key: 'myAtom',
      default: 2
    })
    const myAtomSelector = selector({
      key: 'myAtomSelector',
      get:
        (index) =>
        ({ get }) => {
          let _myAtom = get(myAtom)
          return `${_myAtom}-${index}`
        }
    })
    function Test9() {
      const myAtomSele = useRecoilValue(myAtomSelector(9)) // 默认情况下不能给selector中的方法传递参数
      return (
        <div>
          <h2>测试默认情况下是否可以给get方法传递参数</h2>
          <p>{myAtomSele}</p>
        </div>
      )
    }


网络异常,图片无法展示
|


正确的方式


const myAtom = atom({
  key: 'myAtom',
  default: 2
})
// const myAtomSelector = selector({
//   key: 'myAtomSelector',
//   get:
//     (index) =>
//     ({ get }) => {
//       let _myAtom = get(myAtom)
//       return `${_myAtom}-${index}`
//     }
// })
const myAtomSelector = selectorFamily({
  key: 'myAtomSelector',
  get:
    (index) =>
    ({ get }) => {
      let _myAtom = get(myAtom)
      return `${_myAtom}-${index}`
    }
})
function Test9() {
  const myAtomSele = useRecoilValue(myAtomSelector(9))
  return (
    <div>
      <h2>测试默认情况下是否可以给get方法传递参数</h2>
      <p>{myAtomSele}</p>
    </div>
  )
}


网络异常,图片无法展示
|


useRecoilCallback


这个钩子类似于 useCallback(),但将为你的回调提供一个 API,以便与 Recoil 状态一起工作。这个钩子可以用来构造一个回调,这个回调可以访问 Recoil 状态的只读 Snapshot,并且能够异步更新当前的 Recoil 状态。


就是在当异步操作拿到数据后,我们就可以更新state。


const itemsInCartState = atom({
      key: 'itemsInCartState',
      default: {}
    })
    const itemsInCart = selector({
      key: 'itemsInCart',
      get: async () => {
        const res = await axios('http://myjson.dit.upm.es/api/bins/fo6v')
        return res.data
      }
    })
    function Test11() {
    // 返回一个函数
      const logCartItems = useRecoilCallback(({ snapshot, set }) => async () => {
        // 异步获取异步数据
        const numItemsInCart = await snapshot.getPromise(itemsInCart)
        // 获取到数据后更新state
        set(itemsInCartState, numItemsInCart)
      })
      const items = useRecoilValue(itemsInCartState)
      return (
        <div>
          {items.name ? <p>{items.name}</p> : <p>暂无数据</p>}
          <button onClick={logCartItems}>获取数据</button>
        </div>
      )
    }


网络异常,图片无法展示
|


waitForAll


这个api就是可以让我们并发去获取异步数据。但是在他内部不能直接书写网络请求。因为他的参数就只能是一个包含若干RecoilValue值的数组 或者 一个对象,对象属性值都是RecoilValue值。


const friendsInfoQuery = selector({
      key: 'FriendsInfoQuery',
      // 这种写法会报错。
      get: async ({ get }) => {
        const friends = waitForAll([
          await axios('http://myjson.dit.upm.es/api/bins/albj'),
          await axios('http://myjson.dit.upm.es/api/bins/fo6v')
        ])
        return friends
      }
    })


网络异常,图片无法展示
|


function myDBQuery(params) {
      return axios('http://myjson.dit.upm.es/api/bins/' + params)
    }
    // 异步请求的selector
    const userInfoQuery = selectorFamily({
      key: 'UserInfoQuery',
      get: (params) => async () => {
        const response = await myDBQuery(params)
        if (response.error) {
          throw response.error
        }
        return response.data
      }
    })
    let paramss = ['albj', 'fo6v']
    // 并发获取异步数据
    const friendsInfoQuery = selector({
      key: 'FriendsInfoQuery',
      get: async ({ get }) => {
        const friends = get(waitForAll(paramss.map((item) => userInfoQuery(item))))
        return friends
      }
    })
    function AllRequest() {
      const value = useRecoilValue(friendsInfoQuery)
      return (
        <div>
          <p>异步</p>
          <Suspense fallback={<div>loading...</div>}>
            {value.map((item) => (
              <div key={item.name}>
                <h1>{item.name}</h1>
                <p>{item.age}</p>
              </div>
            ))}
          </Suspense>
        </div>
      )
    }


相关文章
|
6天前
|
前端开发 JavaScript UED
React 图标库使用指南
本文详细介绍如何在 React 项目中使用 `react-icons` 等图标库,涵盖环境搭建、基础使用、常见问题与易错点、高级用法等内容,并通过代码案例进行说明。适合初学者和进阶开发者参考。
25 8
|
1月前
|
前端开发 JavaScript API
React开发需要了解的10个库
本文首发于微信公众号“前端徐徐”,介绍了React及其常用库。React是由Meta开发的JavaScript库,用于构建动态用户界面,广泛应用于Facebook、Instagram等知名网站。文章详细讲解了Axios、Formik、React Helmet、React-Redux、React Router DOM、Dotenv、ESLint、Storybook、Framer Motion和React Bootstrap等库的使用方法和应用场景,帮助开发者提升开发效率和代码质量。
119 4
React开发需要了解的10个库
|
1月前
|
前端开发 JavaScript
React学习之——条件渲染
【10月更文挑战第16天】React 中没有像Vue中v-if这种指令。React 中的条件渲染和 JavaScript 中的一样,使用 JavaScript 运算符 if 或者条件运算符去创建元素来表现当前的状态,然后让 React 根据它们来更新 UI。
|
2月前
|
前端开发 JavaScript
学习react基础(3)_setState、state、jsx、使用ref的几种形式
本文探讨了React中this.setState和this.state的区别,以及React的核心概念,包括核心库的使用、JSX语法、类与函数组件的区别、事件处理和ref的使用。
68 3
学习react基础(3)_setState、state、jsx、使用ref的几种形式
|
2月前
|
前端开发
学习react基础(2)_props、state和style的使用
本文介绍了React中组件间数据传递的方式,包括props和state的使用,以及如何在React组件中使用style样式。
33 0
|
22天前
|
前端开发 JavaScript 安全
学习如何为 React 组件编写测试:
学习如何为 React 组件编写测试:
36 2
|
1月前
|
资源调度 前端开发 JavaScript
React进阶学习
React进阶学习
14 1
|
1月前
|
资源调度 前端开发 JavaScript
React中classnames库使用
【10月更文挑战第7天】
|
23天前
|
资源调度 前端开发 JavaScript
React 测试库 React Testing Library
【10月更文挑战第22天】本文介绍了 React Testing Library 的基本概念和使用方法,包括安装、基本用法、常见问题及解决方法。通过代码案例详细解释了如何测试 React 组件,帮助开发者提高应用质量和稳定性。
35 0
|
1月前
|
JSON 前端开发 JavaScript
React 进阶阶段学习计划
React 进阶阶段学习计划