惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园_首页
I
InfoQ
The Register - Security
The Register - Security
L
LangChain Blog
H
Help Net Security
The GitHub Blog
The GitHub Blog
S
Schneier on Security
博客园 - 【当耐特】
W
WeLiveSecurity
Attack and Defense Labs
Attack and Defense Labs
IT之家
IT之家
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Google DeepMind News
Google DeepMind News
The Cloudflare Blog
H
Heimdal Security Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Y
Y Combinator Blog
雷峰网
雷峰网
N
Netflix TechBlog - Medium
Security Archives - TechRepublic
Security Archives - TechRepublic
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
L
Lohrmann on Cybersecurity
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
T
The Exploit Database - CXSecurity.com
P
Privacy & Cybersecurity Law Blog
G
GRAHAM CLULEY
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
V
Visual Studio Blog
博客园 - 聂微东
PCI Perspectives
PCI Perspectives
Last Week in AI
Last Week in AI
A
Arctic Wolf
宝玉的分享
宝玉的分享
T
The Blog of Author Tim Ferriss
S
Secure Thoughts
T
Threat Research - Cisco Blogs
GbyAI
GbyAI
云风的 BLOG
云风的 BLOG
D
Darknet – Hacking Tools, Hacker News & Cyber Security
S
SegmentFault 最新的问题
SecWiki News
SecWiki News
月光博客
月光博客
大猫的无限游戏
大猫的无限游戏
Schneier on Security
Schneier on Security
P
Proofpoint News Feed
博客园 - Franky
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
AI
AI
Engineering at Meta
Engineering at Meta

青空之蓝

[青空之蓝-2023] - 色彩 | 青空之蓝 [青空之蓝-2022] - 平静 | 青空之蓝 [青空之蓝-2021] - 远望 | 青空之蓝 浅谈垃圾回收 | 青空之蓝 浅谈泛型擦除 | 青空之蓝 浅谈单点登录 | 青空之蓝 使用 Kotlin 编写 Spring 测试 | 青空之蓝 设计模式系列文章 | 青空之蓝 从零实现一个 Java 微框架 - IoC | 青空之蓝 从零实现一个 Java 微框架 - 前言 | 青空之蓝 浅谈 JVM:类加载 | 青空之蓝 浅谈 IO | 青空之蓝 浅谈并发:synchronized & ReentrantLock | 青空之蓝 浅谈并发:CAS & AQS | 青空之蓝 浅谈并发:ThreadLocal | 青空之蓝 浅谈并发:三大特性 | 青空之蓝 浅谈组合注解 & 注解别名 | 青空之蓝 [青空之蓝-2020]-迷茫 | 青空之蓝 Java 系列文章 | 青空之蓝 HTTP 系列文章 | 青空之蓝 浅谈 EatWhatYouKill | 青空之蓝 浅谈可扩展线程池 | 青空之蓝 聊聊写框架 | 青空之蓝 聊聊现状-[2020-09] | 青空之蓝 浅谈并发:锁 | 青空之蓝 浅谈并发:基础 | 青空之蓝 浅谈缓存 | 青空之蓝 无须定义类,Spring 快速注入 Json 参数 | 青空之蓝 浅谈 Proxy 和 Aop | 青空之蓝 从零实现一个 PHP 微框架 - 初始化请求 | 青空之蓝 为 Vue3 添加一个简单的 Store | 青空之蓝 从零实现一个 PHP 微框架 - 服务提供者 | 青空之蓝 WSL2 踩坑记录 | 青空之蓝 浅谈浏览器Event Loop [更新] | 青空之蓝 从零实现一个 PHP 微框架 - Bootstrap 启动加载 | 青空之蓝 从零实现一个 PHP 微框架 - IoC 容器 | 青空之蓝 从零实现一个 PHP 微框架 - PSR & Composer | 青空之蓝 从零实现一个 PHP 微框架 - 前言 | 青空之蓝 MVVM 简单实现 | 青空之蓝 浅谈 DI 和 IoC | 青空之蓝 中间件实现 [PHP] | 青空之蓝 告别 Windows 终端的难看难用,打造好用的 PowerShell | 青空之蓝 VSCode Java输出中文乱码问题解决[更新] | 青空之蓝 浅谈浏览器渲染 | 青空之蓝 Vue-Cli@2 项目迁移日志 | 青空之蓝 Laragon & Scoop 集成踩坑记录 | 青空之蓝 「一行代码」优雅管理 Windows 软件 | 青空之蓝 [青空之蓝-2019]-年度总结 | 青空之蓝 为Vue添加简单的Store | 青空之蓝 为Vuex添加同步Action | 青空之蓝 浅谈B+树 | 青空之蓝 浅谈跳表 | 青空之蓝 浅谈数据库索引 | 青空之蓝 MySQL事务隔离 | 青空之蓝 算法复杂度分析(1) | 青空之蓝 一年来的经验总结 | 青空之蓝 Acrylic - VSCode Extension | 青空之蓝 ace编辑器设置惯性滚动 | 青空之蓝 Java二叉树实现 | 青空之蓝 Java图实现 | 青空之蓝 为apt方式安装的nginx重新编译增加WebDAV | 青空之蓝 XK-Editor - 一个支持富文本和Markdown的编辑器 | 青空之蓝 JS生成列表树 | 青空之蓝 Laravel生成目录树 | 青空之蓝 XK-Note - 集各种神奇功能的云笔记 | 青空之蓝 PHP GD生成验证码 | 青空之蓝 PHP GD图片处理[转换格式-水印-缩略图] | 青空之蓝 Origami - 简洁轻快的WordPress主题 | 青空之蓝 为WordPress启用WorkBox | 青空之蓝 [青空之蓝-2018]-年度总结 | 青空之蓝 VSCode Java手动导入jar和源码包 | 青空之蓝 Windows IP变化自动发送邮件 | 青空之蓝 C链表实现重制版 | 青空之蓝 C 结构体的定义和使用 | 青空之蓝 图的搜索(遍历) - BFS & DFS | 青空之蓝 Java链表实现 | 青空之蓝 C 快速排序 | 青空之蓝 C 插入排序 | 青空之蓝 C 归并排序 | 青空之蓝 C语言链表实现 | 青空之蓝 VSCode配置Java调试环境[Windows] | 青空之蓝 C 选择排序 | 青空之蓝 C 冒泡排序 | 青空之蓝 VSCode配置PHP调试环境[Windows] | 青空之蓝 VSCode配置C/C++ GDB调试环境[Windows] | 青空之蓝 WordPress友情链接模板 | 青空之蓝 Intel Optane 傲腾内存体验 | 青空之蓝 Mysql双机热备实战 | 青空之蓝 博客一年记录 | 青空之蓝 为WordPress启用Service Worker | 青空之蓝 Bing每日一图API | 青空之蓝 iframe延迟加载 | 青空之蓝 写在2018年高考前 | 青空之蓝 The Fox主题汉化分享 | 青空之蓝 [青空之蓝-2017]-崭新 | 青空之蓝 本博客评论规则 | 青空之蓝 世界,您好! | 青空之蓝
为React添加简单的Store | 青空之蓝
Otstar Lin · 2019-11-29 · via 青空之蓝

前言

前几天打算使用 React 开发一个博客系统,由于有许多数据都是要共用的,比如 Tags,Categories 等,而 React 相比 Vue 会组件嵌套的情况会更严重,如果将数据一个个转发势必会造成代码逻辑过于复杂,耦合度过高,所以,我们需要全局状态管理。React 有一个好伙伴 Redux,Redux 是一个用于应用程序状态管理的开源 JavaScript 库。但是 Redux 相对复杂,需要编写太多模板代码,而博客系统这种轻应用并没必要使用 Redux。这时就需要自制一个简单的 Store 来管理全局状态。

分析

在正式编写代码之前我们需要先分析一下我们的 Store 需要什么功能,以及结构。

首先 Store 需要有 State 来存储数据,Action 来触发全局的事件,以及对 State 的更改,我们并不需要 Reduce,有 Reduce 可以很好的分辨 State 的变动,但是我们的应用并没有这么复杂,所以并不需要多一步 Reduce 来改变 State。

由于我使用过 Redux 和 Vuex,通过对比可以发现 Vuex 的 Action 调用相对简单灵活,但是并不能很好的应对同步的场景,所以为了后期的开心编码,我们需要结合二者的一些优点。

React 在 16.x 添加了 Context,使得我们在组件树中传递数据能够简单的实现,我们只需要将 root 组件的状态作为全局状态通过 Context 传递下去即可。

实现

首先,我们需要初始化 State 和 Action,或许你会对下方的 this 感到疑惑,耐心看完下面的代码就能理解了。

// initialStore.js

export const initialState = {
  count: 1,
};

export const initialActions = {
  addCount() {
    this.setState({ ...this.state, count: this.state.count + 1 });
  },
};
// initialStore.js

export const initialState = {
  count: 1,
};

export const initialActions = {
  addCount() {
    this.setState({ ...this.state, count: this.state.count + 1 });
  },
};

然后我们需要导入 initialStore 并创建 Context,同时导出 Context,用于子组件

import React from "react";
import { initialActions, initialState } from "./initialStore";

export const StoreContext = React.createContext({
  state: initialState,
  actions: initialActions,
});
import React from "react";
import { initialActions, initialState } from "./initialStore";

export const StoreContext = React.createContext({
  state: initialState,
  actions: initialActions,
});

这时我们就能创建一个 Store 组件来存储和转发 Context

export default class StoreProvider extends React.Component {
  constructor(props) {
    super(props);
    this.state = initialState;
    this.actions = initialActions;
    for (const key in this.actions) {
      this.actions[key] = this.actions[key].bind(this);
    }
  }

  render() {
    return (
      <StoreContext.Provider
        value={{
          state: this.state,
          actions: this.actions,
        }}
      >
        {this.props.children}
      </StoreContext.Provider>
    );
  }
}
export default class StoreProvider extends React.Component {
  constructor(props) {
    super(props);
    this.state = initialState;
    this.actions = initialActions;
    for (const key in this.actions) {
      this.actions[key] = this.actions[key].bind(this);
    }
  }

  render() {
    return (
      <StoreContext.Provider
        value={{
          state: this.state,
          actions: this.actions,
        }}
      >
        {this.props.children}
      </StoreContext.Provider>
    );
  }
}

使用 Class 组件的原因是为了将该组件的 this 绑定到 Action 上,这样 Action 就能通过 this.state 和 this.setState 操作读取 State 了,你也可以使用 Hook 来达到相同的目的,只是需要利用 useRef 来防止函数读取到旧的状态,使用起来比 Class 复杂,所以这里直接使用 Class 了。

改进

使用全局状态管理在调试的时候不太容易跟踪状态的变化,所以我们需要在状态变化的时候打印变动信息,让调试更方便。

export default class StoreProvider extends React.Component {
  constructor(props) {
    super(props);
    this.state = initialState;
    if (process.env.NODE_ENV === "development") {
      this.defSetState = this.setState;
      this.setState = this.debugSetState;
    }
    this.actions = initialActions;
    for (const key in this.actions) {
      this.actions[key] = this.actions[key].bind(this);
    }
  }

  debugSetState(newState) {
    try {
      throw new Error("[!] - 检查到未被移除的Log调用:");
    } catch (e) {
      console.log({
        oldState: this.state,
        newState: newState,
        dispatchAction: e.stack,
      });
    }
    if (window.__REACT_DISPATCH_SHOW_TRACE__) {
      console.trace();
    }
    this.defSetState(newState);
  }

  render() {
    return (
      <StoreContext.Provider
        value={{
          state: this.state,
          actions: this.actions,
        }}
      >
        {this.props.children}
      </StoreContext.Provider>
    );
  }
}
export default class StoreProvider extends React.Component {
  constructor(props) {
    super(props);
    this.state = initialState;
    if (process.env.NODE_ENV === "development") {
      this.defSetState = this.setState;
      this.setState = this.debugSetState;
    }
    this.actions = initialActions;
    for (const key in this.actions) {
      this.actions[key] = this.actions[key].bind(this);
    }
  }

  debugSetState(newState) {
    try {
      throw new Error("[!] - 检查到未被移除的Log调用:");
    } catch (e) {
      console.log({
        oldState: this.state,
        newState: newState,
        dispatchAction: e.stack,
      });
    }
    if (window.__REACT_DISPATCH_SHOW_TRACE__) {
      console.trace();
    }
    this.defSetState(newState);
  }

  render() {
    return (
      <StoreContext.Provider
        value={{
          state: this.state,
          actions: this.actions,
        }}
      >
        {this.props.children}
      </StoreContext.Provider>
    );
  }
}

在 setState 注入输出日志,我们就能在控制台清晰的看到状态的改变,以及执行栈。

那么,如何使用呢?

使用其实就是从 Context 提取出 State 和 Action,把它们当成变量和函数执行即可

const store = useContext(StoreContext);
const { count } = store.state;
const { addCount } = store.actions;

//...

<button onClick={() => addCount()}>{count}</button>;
const store = useContext(StoreContext);
const { count } = store.state;
const { addCount } = store.actions;

//...

<button onClick={() => addCount()}>{count}</button>;

为React添加简单的Store

https://blog.ixk.me/post/add-simple-store-for-react
  • 许可协议

    BY-NC-SA

  • 本文作者

    Otstar Lin

  • 发布于

    2019/11/29

转载或引用本文时请遵守许可协议,注明出处、不得用于商业用途!

为Vue添加简单的Store为Vuex添加同步Action