这几天在找工作,昨天晚上联系了一个字节的面试岗位hr,然后推荐了react的两个状态管理库。感觉。
我们都知道react-redux状态管理的编码方式比较复杂,所以得知这两个状态管理库后,立刻来学习。xdm,学无止境啊,加油。
这里贴一下两个库的中文文档地址
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>
)
}
以上发送的请求接口都是通过myjson
来创建的。平常开发学习时,使用这个网站来做网络请求,真的是舒服,嘻嘻。
以上就是参考官网学习了一些常用的api,再接再厉。