React 如何通过 Hooks 来封装比较复杂的数据? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
HXHL
V2EX    React

React 如何通过 Hooks 来封装比较复杂的数据?

  •  
  •   HXHL 2023-12-23 12:49:22 +08:00 2193 次击
    这是一个创建于 658 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我的用例大概如下。比如拿 book 的数据,可能组件有时候需要一个数组 books 、有时候是一个数据结构 book 。 然后, 拿 books 的条件是 book list id(一个书单 id 下面的所有书)和 user id(用户名下书),两者可以都有,或者有一个。

    然后拿 book 的条件是 book id 。

    我是应该抽象成useBooks(bookListId?:string, userId?: string)useBook(bookId:string)两个 Hooks 还是直接返回一个 book store ,让组件自己拿数据? 这些 Hooks 感觉怎么怎么每次被调用时网络请求和数据更新的问题? 然后我还需要 createBook 、updateBook 之类的函数。

    const { books, isLoading, createBook } = useBooks(xxx,xxx) 

    我之前用 swr 虽然感觉能凑合管理了,修改完数据就得 mutate 来刷新相关的 key 。但是总感觉不太舒服。

    4 条回复    2023-12-24 22:24:17 +08:00
    XCFOX
        1
    XCFOX  
       2023-12-23 14:11:19 +08:00   2
    你可能需要使用 zustand 或者 valtio 等状态管理库。

    我个人是喜欢用 class 封装成 controller 的,然后在组件内使用 valtio 的 proxy 和 useSnapshot 来使用。

    ```ts
    class BookController {
    books: Record<string, Book> = {}

    async getBooks() {
    const books = await fetchBooks()
    books.forEach((book) => {
    this.books[book.id] = book
    })
    }

    async createBook(book: Book) {
    // ...
    }
    async updateBook(id: string, book: Book) {
    // ...
    }
    }
    ```

    在组件内使用:

    ```tsx
    import { proxy, useSnapshot } from 'valtio'

    const YourCompOnent= () => {
    const cOntroller= useMemo(() => proxy(new BookController()), [])
    const { books } = useSnapshot(controller)

    useEffect(() => {
    controller.getBooks()
    }, [controller])

    const allBooks = Object.values(books)
    const OneBook= books['bookId']

    return (
    <div>
    {allBooks.map((book) => (
    <div key={book.id}>{book.name}</div>
    ))}
    <button OnClick={() => controller.createBook(yourBook)}>创建</button>
    </div>
    )
    }
    ```

    valtio: https://github.com/pmndrs/valtio
    zustand: https://github.com/pmndrs/zustand
    theprimone
        2
    theprimone  
       2023-12-24 15:11:45 +08:00
    @XCFOX #1 Jotai 呢?
    XCFOX
        3
    XCFOX  
       2023-12-24 16:23:54 +08:00
    @theprimone #2
    状态管理主要解决的问题有:
    1. 跨组件状态传递
    2. 组织 actionsa ,比如 createBook 、updateBook

    zustand 强制在 create store 的时候组织 actions: https://docs.pmnd.rs/zustand/guides/updating-state

    valtio 很自由,可以用你最熟悉的 js 语法组织 actions: https://valtio.pmnd.rs/docs/how-tos/how-to-organize-actions

    Jotai 完全遵守 React Hooks 规则,本身没有组织 actions 的办法。需要使用 React 闭包 + useCallback 来封装 actions ,或者使用 Reducer 。
    比如上面的 BookController 按 Jotai 的写法就会变成:
    ```ts
    import { atom } from 'jotai'

    const booksAtom = atom<Record<string, Book>>()

    function useBooks() {
    const [books, setBooks] = useAtom(booksAtom)

    const getBooks = useCallback(async () => {
    const books = await fetchBooks()
    setBooks((prevBooks) => {
    const nextBooks = { ...prevBooks }
    books.forEach((book) => {
    nextBooks[book.id] = book
    })
    return nextBooks
    })
    }, [setBooks])

    const createBook = useCallback(
    async (book: Book) => {
    const newBook = await fetch('createBook', { method: 'POST', body: book })
    setBooks((prevBooks) => ({ ...prevBooks, [newBook.id]: newBook }))
    },
    [setBooks]
    )

    const updateBook = useCallback(
    async (id: string, book: Book) => {
    // ...
    },
    [setBooks]
    )

    return { books, getBooks, createBook, updateBook }
    }
    ```

    因为我讨厌 useCllback 以及 Reducer ,所以不推荐 Jotai 。
    theprimone
        4
    theprimone  
       2023-12-24 22:24:17 +08:00
    @XCFOX #3 学习了,其实之前也看过这个 issue https://github.com/pmndrs/zustand/issues/483#issuecomment-876406137 一个组织整这么多状态管理也太秀了,不过后来看到个说法:

    > Zustand 、Jotai 和 Valtio ,它们用起来分别非常像 Redux 、Recoil 和 MobX

    之前有用过 zustand + immer 现在综合各方那我觉得还是这个方法比较合适啊 更 React 一些
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     855 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 22ms UTC 20:33 PVG 04:33 LAX 13:33 JFK 16:33
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86