Sentry 开发者贡献指南 - 前端 React Hooks 与虫洞状态管理模式
系列
什么是虫洞状态管理模式?
您可以逃脱的最小 state
共享量是多少?
保持你的 state
。尽可能靠近使用它的地方。
如果有一个组件关心这个问题,使用它。如果有几个组件在意,就用 props
分享一下。 如果很多组件都关心,把它放在 context
中。
Context
就像一个虫洞。它使您的组件树弯曲,因此相距很远的部分可以接触。
利用自定义 hooks
使这变得容易。
一个例子
构建一个点击计数器。虫洞状态管理模式最好通过示例来解释 ️
CodeSandbox(示例代码)
步骤 1
我们从 useState
开始,因为它是最简单的。
const ClickCounter = () => {
const [count, setCount] = useState(0);
function onClick() {
setCount(count => count + 1);
}
return <button onClick={onClick}>{count} +1</button>;
};
count
保存当前的点击次数,setCount
让我们在每次点击时更新值。
足够简单。
不过,外观并不是很漂亮。让我们用一个自定义按钮组件和一些嵌套来改进它。
步骤 2
我们创建了一个可重复使用的 PrettyButton
,确保您应用中的每个按钮看起来都很棒。
状态保留在 ClickCounter
组件中。
const ClickCounter = () => {
const [count, setCount] = useState(0);
function onClick() {
setCount(count => count + 1);
}
return (
<>
<p>You have clicked buttons {count} times</p>
<div style={{ textAlign: "right" }}>
<PrettyButton onClick={onClick}>+1</PrettyButton>
</div>
</>
);
};
这是必要的最少状态共享。我们也保持了简单的状态。
计数器组件关心点击次数
和计数
,因此它将回调作为 props
传递到按钮中。函数被调用,状态更新,组件重新渲染。
不需要复杂的操作。
步骤 3
如果我们的状态更复杂怎么办? 我们有 2
个属于一起的项。
您可以在您的状态中保留复杂的值。效果很好。
const ClickCounter = () => {
const [count, setCount] = useState({ A: 0, B: 0 });
function onClickA() {
setCount(count => {
return { ...count, A: count.A + 1 };
});
}
function onClickB() {
setCount(count => {
return { ...count, B: count.B + 1 };
});
}
return (
<>
<p>
You have clicked buttons A: {count.A}, B: {count.B} times
</p>
<div style={{ textAlign: "right" }}>
<PrettyButton onClick={onClickA}>A +1</PrettyButton>
<PrettyButton onClick={onClickB}>B +1</PrettyButton>
</div>
</>
);
};
我们已将 count
拆分为一个对象 – { A, B }
。
现在单个状态可以保存多个值。单独按钮点击的单独计数。
React
使用 JavaScript
相等来检测重新渲染的更改,因此您必须在每次更新时制作完整状态的副本。这在大约 10,000
个元素时变慢。
您也可以在这里使用 useReducer
。
特别是当您的状态变得更加复杂并且项目经常单独更新时。
使用 useReducer
的类似状态如下所示:
const [state, dispatch] = useReducer((action, state) => {
switch (action.type) {
case 'A':
return { ...state, A: state.A + 1 }
case 'B':
return { ...state, A: state.A + 1 }
}
}, { A: 0, B: 0})
function onClickA() {
dispatch({ type: 'A' })
}
你的状态越复杂,这就越有意义。
但我认为那些 switch
语句很快就会变得混乱,而且你的回调函数无论如何都已经是动作了。
步骤 4
如果我们想要 2
个按钮更新相同的状态怎么办?
您可以将 count
和 setCount
作为 props
传递给您的组件。但这变得越来越混乱。
const AlternativeClick = ({ count, setCount }) => {
function onClick() {
setCount(count => {
return { ...count, B: count.B + 1 };
});
}
return (
<div style={{ textAlign: "left" }}>
You can also update B here
<br />
<PrettyButton onClick={onClick}>B +1</PrettyButton>
<p>It's {count.B} btw</p>
</div>
);
};
我们创建了一个难以移动并且需要理解太多父逻辑的组件。关注点是分裂的,抽象是奇怪的,我们造成了混乱。
你可以通过只传递它需要的状态部分和一个更自定义的 setCount
来修复它。但这是很多工作。
步骤 5
相反,您可以使用虫洞与自定义 hook
共享状态。
您现在有 2
个共享状态的独立组件。将它们放在您的代码库中的任何位置,它 Just Works
。
需要在其他地方访问共享状态?添加 useSharedCount
hook,瞧。
这是这部分的工作原理。
我们有一个 context provider
,里面有一些操作:
export const SharedCountProvider = ({ children }) => {
// replace with useReducer for more flexiblity
const [state, setState] = useState(defaultState);
const [contextValue, setContextValue] = useState({
state,
// dispatch // from your reducer
// this is where a reducer comes handy when this grows
setSharedCount: (key, val) => {
setState(state => {
return { ...state, [key]: val };
});
}
// other stuff you need in context
});
// avoids deep re-renders
// when instances of stuff in context change
useEffect(() => {
setContextValue(currentValue => ({
...currentValue,
state
}));
}, [state]);
return (
<SharedCountContext.Provider value={contextValue}>
{children}
</SharedCountContext.Provider>
);
};
Context Provider
使用丰富的 state
变量来保持您的状态。
这里对我们来说是 { A, B }
。
contextValue
是一个更丰富的状态,它也包含操作该状态所需的一切。通常,这将是来自您的 reducer
的 dispatch
方法,或者像我们这里的自定义状态设置器。
我们的 setSharedCount
方法获取一个 key
和一个 val
并更新该部分状态。
setSharedCount("B", 10);
然后我们有一个副作用,它观察 state
的变化并在需要时触发重新渲染。这避免了每次我们重新定义我们的 dispatch
方法或其他任何东西时的深度重新渲染。
使 React
树更稳定 ️
在这个 provider
中呈现的每个组件都可以使用这个相同的自定义 hook
来访问它需要的一切。
export function useSharedCount() {
const { state, setSharedCount } = useContext(SharedCountContext);
function incA() {
setSharedCount("A", state.A + 1);
}
function incB() {
setSharedCount("B", state.B + 1);
}
return { count: state, incA, incB };
}
自定义 hook
利用 React Context
共享状态,定义更简单的 incA
和 incB
辅助方法,并返回它们的状态。
这意味着我们的 AlternativeClick
组件可以是这样的:
import {
useSharedCount
} from "./SharedCountContextProvider";
const AlternativeClick = () => {
const { count, incB } = useSharedCount();
return (
<div style={{ textAlign: "left" }}>
You can also update B here
<br />
<PrettyButton onClick={incB}>B +1</PrettyButton>
<p>It's {count.B} btw</p>
</div>
);
};
从自定义 hook
获取 count
和 incB
。使用它们。
性能怎么样?
很好。
尽可能少地共享 state
。对应用程序的不同部分使用不同的 context provider
。
不要让它成为 global
,除非它需要是 global
的。包裹你可以逃脱的树的最小部分。
复杂度如何?
什么复杂度?保持小。不要把你不需要的东西塞进去。
讨厌管理自己的状态
看到我们 SharedCountProvider
中处理状态变化的部分了吗? 这部分:
const [contextValue, setContextValue] = useState({
state,
// dispatch // from your reducer
// this is where a reducer comes handy when this grows
setSharedCount: (key, val) => {
setState(state => {
return { ...state, [key]: val };
});
}
// other stuff you need in context
});
为此,您可以使用 XState
。或者 reducer
。甚至 Redux
,如果你真的想要的话。
不过,如果你使用 Redux
,你不妨一路走下去
顶级开源项目是如何使用的?(Sentry)
organizationContext.tsx(详细代码)
Refs
Sentry 开发者贡献指南 - 前端 React Hooks 与虫洞状态管理模式的更多相关文章
- Sentry 开发者贡献指南 - 前端(ReactJS生态)
内容整理自官方开发文档 系列 1 分钟快速使用 Docker 上手最新版 Sentry-CLI - 创建版本 快速使用 Docker 上手 Sentry-CLI - 30 秒上手 Source Map ...
- Sentry 开发者贡献指南 - SDK 开发(性能监控)
内容整理于官方开发文档 系列 Docker Compose 部署与故障排除详解 K8S + Helm 一键微服务部署 Sentry 开发者贡献指南 - 前端(ReactJS生态) Sentry 开发者 ...
- Sentry 开发者贡献指南 - SDK 开发(事件负载)
内容整理自官方开发文档 系列 Docker Compose 部署与故障排除详解 1 分钟快速使用 Docker 上手最新版 Sentry-CLI - 创建版本 快速使用 Docker 上手 Sentr ...
- Sentry 开发者贡献指南 - 后端服务(Python/Go/Rust/NodeJS)
内容整理自官方开发文档 系列 1 分钟快速使用 Docker 上手最新版 Sentry-CLI - 创建版本 快速使用 Docker 上手 Sentry-CLI - 30 秒上手 Source Map ...
- Sentry 开发者贡献指南 - Feature Flag
功能 flag 在 Sentry 的代码库中声明. 对于自托管用户,这些标志然后通过 sentry.conf.py 进行配置. 对于 Sentry 的 SaaS 部署,Flagr 用于在生产中配置标志 ...
- Sentry 开发者贡献指南 - SDK 开发(性能监控:Sentry SDK API 演进)
内容整理自官方开发文档 本文档的目标是将 Sentry SDK 中性能监控功能的演变置于上下文中. 我们首先总结了如何将性能监控添加到 Sentry 和 SDK, 然后我们讨论 identified ...
- Sentry 开发者贡献指南 - 配置 PyCharm
概述 如果您使用 PyCharm 进行开发,则需要配置一些内容才能运行和调试. 本文档描述了一些对 sentry 开发有用的配置 配置 Python 解释器:(确保它是 venv 解释器)例如 ~/v ...
- Sentry 开发者贡献指南 - 数据库迁移
Django 迁移是我们处理 Sentry 中数据库更改的方式. Django 迁移官方文档:https://docs.djangoproject.com/en/2.2/topics/migratio ...
- Sentry 开发者贡献指南 - Django Rest Framework(Serializers)
Serializer 用于获取复杂的 python 模型并将它们转换为 json.序列化程序还可用于在验证传入数据后将 json 反序列化回 Python 模型. 在 Sentry,我们有两种不同类型 ...
随机推荐
- [noi706]Sabotage
先可以将所有出度为0的节点连向一个点,然后问题变为求到这个点的必经之点这其实是一道模板题,因为有一个东西叫做支配树容易发现一个点的必经之点都是一条链,其实可以把这条链上最浅的点作为这个点的父亲,那么一 ...
- 解决springboot启动日志异常问题
问题描述:springboot启动异常,启动后没有日志打印. 问题原因:slf4j日志实现重复,找不到对应实现类. 问题应对: 1. 是不是项目没起来---->打印的日志数据,到这里就不打印了, ...
- 面向对象编程之Python学习一
在实际的程序设计中,使用Java面向对象编程方法编写算法能够很清楚的理解其来龙去脉. 习惯了面向对象思维,学习Python也自然使用这种思维. 目前,由于Python很多软件包能够容易的获取和利用,人 ...
- CF1511E Colorings and Dominoes
考虑计数拆开贡献. 因为在一个方案中一个格子最多只会贡献一次,那么不妨反过来求这个格子贡献了多少次. 然后发现,行列独立,那么我们单独计算红蓝色,即可. 一个偶数块贡献当且仅当前面也是偶数块. 然后显 ...
- 洛谷 P6199 - [EER1]河童重工(点分治+虚树)
洛谷题面传送门 神仙题. 首先看到这样两棵树的题目,我们肯定会往动态树分治的方向考虑.考虑每次找出 \(T_2\) 的重心进行点分治.然后考虑跨过分治中心的点对之间的连边情况.由于连边边权与两棵树都有 ...
- LOJ 2372 -「CEOI2002」臭虫集成电路公司(轮廓线 dp)
题面传送门 u1s1 似乎这题全网无一题解?那就由我来写篇题解造福人类罢(伦敦雾 首先看这数据范围,一脸状压.考虑到每一层的状态与上面两层有关,因此每层转移到下一层的有用信息只有两层,需要用三进制保存 ...
- keyboard-interactive authentication with the ssh2 server failed 的SecureCRT报错解决
两种解决方法: 一.选定SSH2,选择Authentication,勾选Password,然后将该选项上移,挪到第一位即可 或者: 二.服务器端修改配置 默认情况/etc/ssh/sshd_confi ...
- kubernetes部署kube-controller-manager服务
本文档介绍部署高可用 kube-controller-manager 集群的步骤. 该集群包含 3 个节点,启动后将通过竞争选举机制产生一个 leader 节点,其它节点为阻塞状态.当 leader ...
- 也谈string.Join和StringBuilder的性能比较
前几天在园子里面看到一篇讲StringBuilder性能的文章.文章里面给出了一个测试用例,比较StringBuilder.AppendJoin和String.Join的性能.根据该测试结果,&quo ...
- 以VuePress的v1.x为基础开发-用户手册
首先配置.vuepress中的config.js module.exports = { title:"用户手册", description: '用户手册', evergreen: ...