更轻巧的状态管理工具——Hookstate

奇舞精选 发表于 7月以前  | 总阅读数:580 次

Hookstate

前言

作为 React 开发人员,管理中型应用程序的状态可能很困难。在开发小型应用程序时,将状态从一个组件传递到另一个组件相对简单。当应用程序的规模发生变化时,就会变得不方便,因为你需要无层级关系组件状态的互相访问支持。

Hookstate是一个完全基于React状态hook的状态管理库。它实施简单、快速、直接且可扩展。不需要模版,它也可以在 Next.js 应用程序中使用。

在本文中,我们将了解如何使用这个库,它是最用户友好的 React 状态管理库之一。

开始使用 Hookstate

在本节中,我们将使用下面的代码块创建一个 React 应用程序:

npx create-react-app react-hookstate
cd react-hookstate

要安装所需的库,请使用以下代码块之一:

npm install --save @hookstate/core @chakra-ui/react @emotion/react @emotion/styled framer-motion axios

或者

yarn add  @hookstate/core @chakra-ui/react @emotion/react @emotion/styled framer-motion axios

Local state

一般来说,当父组件、子组件或仅父组件使用状态时,建议在 React 应用程序中使用本地状态。当多个组件共享一个状态并且应用程序中的每个组件都可以通过这种方式访问该状态时,建议使用全局状态。

为了展示 Hookstate 如何在本地处理状态,我们将利用 useHookstate.

import React from "react";
import { useHookstate } from "@hookstate/core";
import { Box, Button, Flex, Text } from "@chakra-ui/react";

const App = () => {
  const state = useHookstate(0);
  return (
    <Box
      display="flex"
      flexDirection="column"
      justifyContent="center"
      alignItems="center"
      maxW="1440px"
      minH="100vh"
      m="auto"
    >
      <Text textAlign="center" fontWeight="700" fontSize={{base: "32px", md: "64px"}}>
        Counter value: {state.get()}{" "}
      </Text>
      <Flex gap={4}>
        <Button onClick={() => state.set((p) => p + 1)} bg="green" color="#fff">Increment</Button>
        <Button onClick={() => state.set((p) => p - 1)} bg="red" color="#fff">Decrement</Button>
      </Flex>
    </Box>
  );
};

export default App;

上面的代码显示了一个计数器应用程序以及我们如何使用 useHookstate,给变量赋值后state,我们将默认值设置为 0 并使用 set 和 get 方法 useHookstate。set 方法用于改变状态,而 get 方法获取状态的值。

Global state

在本节中,我们将了解如何在全局级别管理应用程序中的状态。与上一节一样,这次状态将是全局的,并且可以从应用程序中的任何位置访问。

创建一个新目录src/store/index.js并将以下代码块粘贴到其中:

import { hookstate, useHookstate } from "@hookstate/core";

const initialState = hookstate({
  count: 0,
});

export const useGlobalState = () => {
  const state = useHookstate(initialState);

  return {
    getCount: () => state.count.value,
    increment: () => {
      state.count.set((count) => count + 1);
    },
    decrement: () => {
      state.count.set((count) => count - 1);
    },
  };
};

在前面的代码块中,我们声明了应用程序的 initialState,它有一个包含 的对象 count,其值设置为0。

接下来,我们创建了一个名为 useGlobalState 的自定义 hook,并将参数传递 initialState 给useHookstate。我们在返回块中有三个函数来读取和修改状态。

要全局访问状态,我们必须首先修改App.js组件。

import React from "react";
import { Box, Button, Flex, Text } from "@chakra-ui/react";
import { useGlobalState } from "./store";

const App = () => {
  const state = useGlobalState();

  const increment =()=> {
    state.increment()
  }

  const decrement =()=> {
    state.decrement()
  }

  return (
    <Box
      display="flex"
      flexDirection="column"
      justifyContent="center"
      alignItems="center"
      maxW="1440px"
      minH="100vh"
      m="auto"
    >
      <Text textAlign="center" fontWeight="700" fontSize={{base: "32px", md: "64px"}}>
        Counter value: {state.getCount()}{" "}
      </Text>
      <Flex gap={4}>
        <Button onClick={() => increment()} bg="green" color="#fff">Increment</Button>
        <Button onClick={() => decrement()} bg="red" color="#fff">Decrement</Button>
      </Flex>
    </Box>
  );
};

export default App;

我们现在可以通过访问全局状态 useGlobalState,这是从 导入的自定义 hook,我们将其设置为在更新的组件中src/store/index.js调用的变量。我们现在可以全局访问读取和改变状态。stateApp.js

具有 CRUD 功能的全局状态

在本节中,我们将创建一个 CRUD 应用程序。这将展示如何管理应用程序状态的真实示例。

我们将创建一个简单的博客应用程序,第一种方法是创建应用程序的状态和改变它的函数。

将以下代码块添加到 src/store/index 文件中。

import { hookstate, useHookstate } from "@hookstate/core";

const initialState = hookstate({
  blog: [],
});

export const useGlobalState = () => {
  const state = useHookstate(initialState);

  return {
    getCountBlog: () => state.blog.length,
    addBlog: (blog) => {
      state.blog.merge([blog]);
    },
    updateBlog: (id, blog) => {
      state.blog.set((b) =>
        b.map((blogs) => {
          if (blogs.id === id) {
            blogs.content = blog.content;
          }
          return blogs;
        })
      );
    },
    deleteBlog: (id) => {
      state.blog.set((blogs) => blogs.filter((blog) => blog.id !== id));
    },
    fetchBlogs: () => state.blog,
  };
};

我们在上面的代码块中为应用程序创建了五个不同的函数,这些函数可以读取和修改状态。例如 addBlog,该函数使用 merge,它会部分更新应用程序的现有状态。

我们将在 App.js 中创建与src/store/index.

import React, { useEffect, useState } from "react";
import {
  Box,
  Button,
  Card,
  CardBody,
  CardFooter,
  Flex,
  Image,
  Input,
  Stack,
  Text,
} from "@chakra-ui/react";
import { useGlobalState } from "./store";

const App = () => {
  const state = useGlobalState();
  const [data, setData] = useState([]);
  const [content, setContent] = useState("");
  const [edit, setEdit] = useState(false);
  const [updateId, setUpdateId] = useState(0);

  const fetchBlog = () => {
    setData(state.fetchBlogs());
  };

  useEffect(() => {
    fetchBlog();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  const addBlog = () => {
    const blog = {
      id: state.getCountBlog() + 1,
      content: content,
    };
    state.addBlog(blog);
    setContent("");
  };

  const updateBlog = (id) => {
    const blog = {
      id,
      content,
    };
    state.updateBlog(id, blog);
    setContent("");
    setUpdateId(0);
    setEdit(false);
  };

  const deleteBlog = (id) => {
    state.deleteBlog(id);
  };

  return (
    <Box
      display="flex"
      flexDirection="column"
      justifyContent="flex-start"
      alignItems="center"
      maxW="1440px"
      minH="100vh"
      m="auto"
    >
       <Box
        width={{ base: "auto", md: "700px" }}
        minH="100vh"
        mt="0rem"
        bg={{ base: "transparent", md: "blackAlpha.400" }}
        p={8}
      >
        <Text fontSize="28px" fontWeight="600" mb={4}>
          Blog posts: {state.getCountBlog()}
        </Text>
        <Flex>
          <Input
            name="content"
            value={content}
            onChange={(e) => setContent(e.target.value)}
            errorBorderColor="crimson"
            placeholder="Enter Quote"
            borderInlineEndRadius={0}
          />
          {edit ? (
            <Button
              onClick={() => updateBlog(updateId)}
              borderInlineStartRadius={0}
              bg="green"
              color="#fff"
            >
              Update
            </Button>
          ) : (
            <Button onClick={addBlog} borderInlineStartRadius={0} bg="green" color="#fff">
              Add
            </Button>
          )}
        </Flex>
        <Box my={8}>
          {data.length < 1 && (
            <Text py={4} textAlign="center">
              No blog post found
            </Text>
          )}
          {data &&
            data.map((item, index) => (
              <Card
                key={index}
                direction={{ base: "column", sm: "row" }}
                overflow="hidden"
                variant="outline"
                my={4}
              >
                <Image
                  objectFit="cover"
                  maxW={{ base: "100%", sm: "200px" }}
                  src="https://images.unsplash.com/photo-1667489022797-ab608913feeb?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHw5fHx8ZW58MHx8fHw%3D&auto=format&fit=crop&w=800&q=60"
                  alt="Caffe Latte"
                />

                <Stack w="full">
                  <CardBody>
                    <Text fontSize="24px" fontWeight="600" py="2">
                      {item.get(item).content}
                    </Text>
                  </CardBody>

                  <CardFooter display="flex" justifyContent="flex-end" gap={4}>
                    <Button
                      onClick={() => {
                        setContent(item.get(item).content);
                        setEdit(true);
                        setUpdateId(item.get(item).id);
                      }}
                      bg="blue"
                      color="#fff"
                    >
                      Edit
                    </Button>
                    <Button
                      onClick={() => deleteBlog(item.get(item).id)}
                      bg="red"
                      color="#fff"
                    >
                      Delete
                    </Button>
                  </CardFooter>
                </Stack>
              </Card>
            ))}
        </Box>
      </Box>
    </Box>
  );
};

export default App;

我们在前面的代码块中创建了命名函数,并将函数从全局状态传递到每个函数中。只需使用item.get(item).content 或 item.value.content 在 Hookstate 中显示一个值。

image text

异步状态

Hookstate 可以轻松处理异步数据,执行 API 调用直至解析。异步数据可以全局存储,并可以从应用程序中的任何位置从存储中访问。

我们将使用下面的代码块创建一个异步状态,该状态将从 API 获取用户列表并在 App.js 组件中显示结果。

import { hookstate, useHookstate } from "@hookstate/core";
import axios from "axios";

const initialState = hookstate(
  {
    loading: false,
    users: [],
  }
);

export const useGlobalState = () => {
  const state = useHookstate(initialState);
  const resourcePath = "https://jsonplaceholder.typicode.com/users";

  return {
    loading: () => state.loading,
    getUsers: async () => {
    await axios.get(resourcePath).then((r) => state.users.set(r.data));
    state.loading.set(true)
    },
    fetchUsers: () => state.users,
  };
};

我们将用户添加到应用程序的初始状态,并使用上面的代码块将值设置为空数组。该数组将包含从 API 检索的所有用户的列表。我们还添加了 state loading,其默认值为 false。

接下来,创建了三个可以读取和修改状态的函数。加载函数读取加载状态;getUsers必须作为访问状态的承诺来处理。fetchUsers只是返回用户的当前状态。

import React, { useEffect, useState } from "react";
import {
  Box,
  Card,
  CardBody,
  CardFooter,
  Image,
  Stack,
  Text,
} from "@chakra-ui/react";
import { useGlobalState } from "./store";

const App = () => {
  const state = useGlobalState();
  const [user, setUser] = useState([]);

  useEffect(() => {
    state.getUsers();

    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  useEffect(() => {
    if (state.loading().value === true) {
      setUser(state.fetchUsers());
    }
  }, [state]);

  return (
    <Box
      display="flex"
      flexDirection="column"
      justifyContent="flex-start"
      alignItems="center"
      maxW="1440px"
      minH="100vh"
      m="auto"
    >
       <Box
        width={{ base: "auto", md: "700px" }}
        minH="100vh"
        mt="0rem"
        bg={{ base: "transparent", md: "blackAlpha.400" }}
        p={8}
      >
         <Text fontSize="28px" fontWeight="600" mb={4}>
          User Count: {user.length}
        </Text>
        <Box my={8}>
          {user.length < 1 && (
            <Text py={4} textAlign="center">
              No user post found
            </Text>
          )}
          {user &&
            user.map((item, index) => (
              <Card
                key={index}
                direction={{ base: "column", sm: "row" }}
                overflow="hidden"
                variant="outline"
                my={4}
              >
                <Image
                  objectFit="cover"
                  maxW={{ base: "100%", sm: "200px" }}
                  src="https://images.unsplash.com/photo-1667489022797-ab608913feeb?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHw5fHx8ZW58MHx8fHw%3D&auto=format&fit=crop&w=800&q=60"
                  alt="Caffe Latte"
                />

                <Stack w="full">
                  <CardBody>
                    <Text fontSize="24px" fontWeight="600" py="2">
                      {item.value.name}
                    </Text>
                  </CardBody>

                  <CardFooter
                    display="flex"
                    justifyContent="flex-start"
                    gap={4}
                  >
                    <Text>{item.value.email}</Text>
                  </CardFooter>
                </Stack>
              </Card>
            ))}
        </Box>
      </Box>
    </Box>
  );
};

export default App;

和前面一样, getUsers 从全局状态初始化函数并将数据加载到用户中,然后 fetchUsers 仅当 loading 设置为 true 时才能成功检索用户列表。接下来,我们映射来自状态的数据 user 并使用 item.value.name 和item.value.email 来获取每个项目的值。

开发工具

使用开发工具,你可以在 Hookstate 中检查应用程序的状态。它具有已知的最简单的配置;所需要做的就是将第二个参数传递给 hookstate,如下面的代码所示。这对生产应用程序没有不利影响。

...
import { devtools } from "@hookstate/devtools";

const initialState = hookstate(
  {
    loading: false,
    users: [],
  },
  devtools({ key: "my-state-label" })
);
...

结尾

篇幅有限,除以上内容之外 Hookstate 还支持多种自定义扩展,如状态快照、状态校验、数据持久化等。本人使用 Hookstate 开发的体感是优于 redux 系列的。要了解有关 Hookstate 的更多信息,请访问官方文档。

最后觉得文章还不错,帮忙赞一个,多谢。

参考文献

https://hookstate.js.org/ https://blog.openreplay.com/state-management-in-react-with-hookstate/

本文由微信公众号奇舞精选原创,哈喽比特收录。
文章来源:https://mp.weixin.qq.com/s/0-cUZ_DBaMZawBXAVCz0wQ

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:1年以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:1年以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:1年以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:1年以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:1年以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:1年以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:1年以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:1年以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:1年以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:1年以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:1年以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:1年以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:1年以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:1年以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:1年以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:1年以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:1年以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:1年以前  |  398次阅读  |  详细内容 »
 相关文章
为Electron程序添加运行时日志 5年以前  |  20336次阅读
Node.js下通过配置host访问URL 5年以前  |  5892次阅读
用 esbuild 让你的构建压缩性能翻倍 4年以前  |  5782次阅读
 目录