从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>
      )
    }


相关文章
|
1月前
|
存储 前端开发 JavaScript
前端框架与库 - React基础:组件、Props、State
【7月更文挑战第12天】React是JavaScript库,专注UI构建,基于组件化。组件是UI模块,可函数式或类定义。Props是组件间安全传递数据的只读参数,用defaultProps和propTypes保证正确性。State则是组件内部可变数据,用于驱动更新。使用setState()确保正确变更和渲染。了解并妥善处理这些概念是高效React开发的基础。
|
1月前
|
前端开发 JavaScript 开发者
前端框架与库 - React生命周期与Hooks
【7月更文挑战第13天】React 框架革新UI构建,引入Hooks简化组件状态管理和副作用处理。组件生命周期涉及挂载、更新、卸载,对应不同方法,如`componentDidMount`、`shouldComponentUpdate`等,但现在推荐使用`useState`和`useEffect` Hooks。`useEffect`处理副作用,需注意清理和依赖数组。避免问题的关键在于正确使用Hooks和理解其工作模式,以构建高效应用。
|
2月前
|
存储 前端开发 JavaScript
在React中有效地管理组件之间的通信和数据流
在React中有效地管理组件之间的通信和数据流
|
2月前
|
存储 前端开发 JavaScript
React中有效地使用props和state来管理组件的数据和行为
React中有效地使用props和state来管理组件的数据和行为
|
1月前
|
JavaScript 前端开发
react18【系列实用教程】moxb —— 集中状态管理 (2024最新版)
react18【系列实用教程】moxb —— 集中状态管理 (2024最新版)
20 0
|
3月前
|
JavaScript 前端开发 API
如何学习React.js?
【5月更文挑战第27天】如何学习React.js?
55 14
|
3月前
|
前端开发 JavaScript 开发者
在React中,如何利用生命周期方法管理组件的状态和行为?
【5月更文挑战第29天】在React中,如何利用生命周期方法管理组件的状态和行为?
36 3
|
3月前
|
前端开发 JavaScript
在React中,如何通过事件处理函数来管理按钮的点击行为?
【5月更文挑战第28天】在React中,如何通过事件处理函数来管理按钮的点击行为?
39 1
|
3月前
|
前端开发 JavaScript
在React中,如何通过事件处理函数来管理输入框的获取和失去焦点行为?
【5月更文挑战第28天】在React中,如何通过事件处理函数来管理输入框的获取和失去焦点行为?
80 1
|
3月前
|
资源调度 前端开发 JavaScript
React Router:React应用的路由管理
【4月更文挑战第25天】React Router是React的官方路由库,用于管理SPA的路由。它基于组件,将URL映射到React组件,核心概念包括路由、链接和导航。设置路由时,在根组件中使用BrowserRouter或HashRouter,包裹Routes组件,定义Route规则。Link组件用于创建内部链接,实现导航。高级特性包括嵌套路由、参数化路由和编程式导航,如子路由、动态参数和JavaScript控制的导航。掌握React Router能帮助开发者更高效地构建复杂的React应用。

热门文章

最新文章