反应 Easy 状态 - by 升起图
新闻: v6.3.0 修补了可以制成的下流错误 僵尸儿童请更新这个版本,至少可以省去一些头痛。 谢谢!
目录
导 言:波:
React Easy State是一个实用的州管理图书馆,有两个功能和两个配套规则.
- 总是用
view()。 。 。 . - 总是用
store()。 。 。 .
import React from 'react';
import { store, view } from '@risingstack/react-easy-state';
const counter = store({
num: 0,
increment: () => counter.num++
});
export default view(() => (
<button onClick={counter.increment}>{counter.num}</button>
));
这足以让它在需要时自动更新您的观点. 无论你如何构建或改变你的国营商店 任何在战术上有效的代码.
检查这个 TodoMVC 代码和框 或者说 原始代码 一个更令人兴奋的例子是嵌入式数据、阵列和getter属性.
安装: cd:
npm install @risingstack/react-easy-state
建立一个快速项目
简单状态支持 创建反应应用 没有额外的配置。 只需运行以下命令即可启动.
npx create-react-app my-app
cd my-app
npm install @risingstack/react-easy-state
npm start
您需要npm 5.2+来使用npx.
每天使用:太阳镜:
创建全球商店
store 从通过的对象创建状态存储并返回它。 州立商店的行为就像通过的对象一样. (确切地说,它是原始对象的透明反应代用. )
import { store } from '@risingstack/react-easy-state';
const user = store({ name: 'Rick' });
// stores behave like normal JS objects
user.name = 'Bob';
国营商店可能具有任意的结构,并且可能以任何战术上有效的方式进行变异.
import { store } from '@risingstack/react-easy-state';
// stores can include any valid JS structure
// including nested data, arrays, Maps, Sets, getters, setters, inheritance, ...
const user = store({
profile: {
firstName: 'Bob',
lastName: 'Smith',
get name() {
return `${user.profile.firstName} ${user.profile.lastName}`;
},
},
hobbies: ['programming', 'sports'],
friends: new Map(),
});
// stores may be mutated in any syntactically valid way
user.profile.firstName = 'Bob';
delete user.profile.lastName;
user.hobbies.push('reading');
user.friends.set('id', otherUser);
Async操作可以用标准Async/await语法来表示.
import { store } from '@risingstack/react-easy-state';
const userStore = store({
user: {},
async fetchUser() {
userStore.user = await fetch('/user');
},
});
export default userStore;
国营商店可以进口和使用其他国营商店.
用户Store.js
import { store } from '@risingstack/react-easy-state';
const userStore = store({
user: {},
async fetchUser() {
userStore.user = await fetch('/user');
},
});
export default userStore;
食谱Store.js
import { store } from '@risingstack/react-easy-state';
import userStore from './userStore';
const recipesStore = store({
recipes: [],
async fetchRecipes() {
recipesStore.recipes = await fetch(
`/recipes?user=${userStore.user.id}`,
);
},
});
export default recipesStore;
包装您的国家仓库 store 尽快.
// DON'T DO THIS
const person = { name: 'Bob' };
person.name = 'Ann';
export default store(person);
// DO THIS INSTEAD
const person = store({ name: 'Bob' });
person.name = 'Ann';
export default person;
第一个例子不会引发重犯 person.name = 'Ann' 变异,因为它针对的是生物体. 变换生- 无 store- 包装的物体 - 不会安排好的.
避免使用 this 国家商店方法的关键词.
import { store, view } from '@risingstack/react-easy-state';
const counter = store({
num: 0,
increment() {
// DON'T DO THIS
this.num++;
// DO THIS INSTEAD
counter.num++;
},
});
export default view(() => (
<div onClick={counter.increment}>{counter.num}</div>
));
this.num++ 没用的,因为 increment 被传递作为回电 失去它 this。您应当使用直接对象引用 - counter - 而不是说 this。 。 。 .
创建被动视图
以 view 把它们变成被动的观点。 反应视图每一次重现一个存储器 - 在其渲染中使用 - 变化.
import React from 'react';
import { view, store } from '@risingstack/react-easy-state';
// this is a global state store
const user = store({ name: 'Bob' });
// this is re-rendered whenever user.name changes
export default view(() => (
<div>
<input
value={user.name}
onChange={ev => (user.name = ev.target.value)}
/>
<div>Hello {user.name}!</div>
</div>
));
用 view - 包括类和功能的 - 即使他们似乎不直接使用商店.
在其渲染过程中使用商店或商店部分部件的每个部件都必须被包裹 view。有时储存的使用并不明确,容易错过.
import { view, store } from '@risingstack/react-easy-state';
const appStore = store({
user: { name: 'Ann' },
});
const App = view(() => (
<div>
<h1>My App</h1>
<Profile user={appStore.user} />
</div>
));
// DO THIS
const Profile = view(({ user }) => <p>Name: {user.name}</p>);
// DON'T DO THIS
// This won't re-render on appStore.user.name = 'newName' like mutations
const Profile = ({ user }) => <p>Name: {user.name}</p>;
如果你是的话 百分百确认 您的组件没有使用任何您可以跳过的任何存储 view 包装.
import React from 'react';
// you don't have to wrap this component with `view`
export default (() => <p>This is just plain text</p>);
view 不过,即使在这种情况下,也建议进行包装.
- 它能让你免于未来的头痛 随着项目的发展 你开始使用这些组件中的商店.
view几乎相当于memo如果你不使用任何商店。 这几乎总是很好的有.
单一活性成分可能在其渲染内使用多个储存.
import React from 'react';
import { view, store } from '@risingstack/react-easy-state';
const user = store({ name: 'Bob' });
const timeline = store({ posts: ['react-easy-state'] });
// this is re-rendered whenever user.name or timeline.posts[0] changes
export default view(() => (
<div>
<div>Hello {user.name}!</div>
<div>Your first post is: {timeline.posts[0]}</div>
</div>
));
view 执行最佳 shouldComponentUpdate (或 memo用于您的组件.
-
使用
PureComponent或者说memo将不提供额外的业绩效益. -
定义自定义
shouldComponentUpdate当您在它内部应用一些特定的用法时,可能很少提供性能效益.
反应成份进行分批处理。 多个同步存储突变不会导致同一组件的多次重接.
import React from 'react';
import { view, store } from '@risingstack/react-easy-state';
const user = store({ name: 'Bob', age: 30 });
function mutateUser() {
user.name = 'Ann';
user.age = 32;
}
// calling `mutateUser` will only trigger a single re-render of the below component
// even though it mutates the store two times in quick succession
export default view(() => (
<div onClick={mutateUser}>
name: {user.name}, age: {user.age}
</div>
));
如果您将您的商店同步进行多次突变 异地任务来源, 多个调值可能很少发生。 如果遇到性能问题,您可以使用 batch 函数. batch(fn) 立即执行已传递的函数,并批次任何后续的重递,直到函数执行完成.
import React from 'react';
import { view, store, batch } from '@risingstack/react-easy-state';
const user = store({ name: 'Bob', age: 30 });
function mutateUser() {
// this makes sure the state changes will cause maximum one re-render,
// no matter where this function is getting invoked from
batch(() => {
user.name = 'Ann';
user.age = 32;
});
}
export default view(() => (
<div>
name: {user.name}, age: {user.age}
</div>
));
注: 反应小组计划在未来改进成品分批处理。 这个
batch函数和内置分批方式在未来可能会被贬值和去除,有利于React自己的分批方式.
总是应用 view 当您将它与其他更高顺序组件结合时,它成为最新的(最内在的)包装器.
import { view } from '@risingstack/react-easy-state';
import { withRouter } from 'react-router-dom';
import { withTheme } from 'styled-components';
const Comp = () => <div>A reactive component</div>;
// DO THIS
withRouter(view(Comp));
withTheme(view(Comp));
// DON'T DO THIS
view(withRouter(Comp));
view(withTheme(Comp));
用(前 v4. 4) 反应路由器 .
-
如果路由没有适当更新,请将您
view(Comp)- 带着Route内部 - 内withRouter(view(Comp))。让反应路由器知道何时更新. -
HOCs的顺序,总是使用
withRouter(view(Comp))。 。 。 .
如果您使用 React 路由器 4.4+ ,则无需此操作 。 你可以找到更多细节 和一些关于这一点的推理 此反应路由文件页面。 。 。 .
使用 React 开发工具 .
如果您想让 React 开发者工具识别您的被动视图组件名称, 您必须通过任意一个 命名函数 或匿名函数 名称推论 页:1 view 包装.
import React from 'react';
import { view, store } from '@risingstack/react-easy-state';
const user = store({
name: 'Rick',
});
const componentName = () => (
<div>{user.name}</div>
);
export default view(componentName);
将巢数据传递给第三方组件.
第三方帮助者,如数据网,可能由许多内部组件组成,这些组件不能由 view,但有时你希望 当通过的数据变异时,他们会重新回复。 传统回放组件在道具因参考而改变时会再发回回回放,因此变换所传回的回放数据在这种情况下不会起作用. 您可以在将可观测数据复制到组件之前,通过深层克隆解决这个问题。 这为每个商店变异的消耗成分创造了新的参考.
import React from 'react';
import { view, store } from '@risingstack/react-easy-state';
import Table from 'rc-table';
import cloneDeep from 'lodash/cloneDeep';
const dataStore = store({
items: [
{
product: 'Car',
value: 12,
},
],
});
export default view(() => (
<Table data={cloneDeep(dataStore.items)} />
));
创建本地仓库
单通全球商店对于像当前用户一样的东西来说是完美的,但有时拥有本地组件状态会更好. 只要在函数组件内创建一个存储,或者在这些情况中作为类组件属性.
函数组件中的本地存储
import React from 'react'
import { view, store } from '@risingstack/react-easy-state'
export default view(() => {
const counter = store({ num: 0 })
const increment = () => counter.num++
return <button={increment}>{counter.num}</button>
})
本地商店在功能上依赖于React钩. 它们要求回放和回放DOM v16.8+或回放原生活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活.
您可以使用反应钩 - 包括 useState - 在功能组件,Easy State 不会干扰它们。 考虑使用 自动效果 代替 useEffect 钩上最好的体验,虽然.
import React from 'react';
import { view, store } from '@risingstack/react-easy-state';
export default view(() => {
const [name, setName] = useState('Ann');
const user = store({ age: 30 });
return (
<div>
<input value={name} onChange={ev => setName(ev.target.value)} />
<input
value={user.age}
onChange={ev => (user.age = ev.target.value)}
/>
</div>
);
});
类组件的本地储存
import React, { Component } from 'react';
import { view, store } from '@risingstack/react-easy-state';
class Counter extends Component {
counter = store({ num: 0 });
increment = () => counter.num++;
render() {
return (
<button onClick={this.increment}>{this.counter.num}</button>
);
}
}
export default view(Counter);
你也可以用香草来 setState 在你的班组,Easy State 不会干扰它.
import React, { Component } from 'react';
import { view, store } from '@risingstack/react-easy-state';
class Profile extends Component {
state = { name: 'Ann' };
user = store({ age: 30 });
setName = ev => this.setState({ name: ev.target.value });
setAge = ev => (this.user.age = ev.target.value);
render() {
return (
<div>
<input value={this.state.name} onChange={this.setName} />
<input value={this.user.age} onChange={this.setAge} />
</div>
);
}
}
export default view(Profile);
不要把本地商店命名为 state它可能与防止国家直接变异的穿插规则相冲突.
import React, { Component } from 'react';
import { view, store } from '@risingstack/react-easy-state';
class Profile extends Component {
// DON'T DO THIS
state = store({});
// DO THIS
user = store({});
render() {}
}
从道具中衍生出本地商店(getDerivedStateFromProps) (中文(简体) ).
包含的类组件 view 拥有额外的静态 deriveStoresFromProps 与香草类似的生命周期方法 getDerivedStateFromProps。 。 。 .
import React, { Component } from 'react';
import { view, store } from '@risingstack/react-easy-state';
class NameCard extends Component {
userStore = store({ name: 'Bob' });
static deriveStoresFromProps(props, userStore) {
userStore.name = props.name || userStore.name;
}
render() {
return <div>{this.userStore.name}</div>;
}
}
export default view(NameCard);
您不应返回一个对象, 而是应当直接改变接收的商店 。 如果您在单个组件上有多个本地商店, 它们都会在第一个道具参数之后作为参数—— 按定义顺序—— 传递 .
高级使用: nerd face :
添加副作用
使用 autoEffect 以自动副作用响应您的存储更改。 自动效果应当包含链末端逻辑——比如将文档标题更改或将数据保存到localStorage. view 是一种特殊的自动效果,可以渲染.
永远不要使用自动效果从其他数据中获取数据. 使用动态获取器代替 .
import { store, autoEffect } from '@risingstack/react-easy-state';
// DON'T DO THIS
const store1 = store({ name: 'Store 1' })
const store2 = store({ name: 'Store 2' })
autoEffect(() => store2.name = store1.name)
// DO THIS INSTEAD
const store1 = store({ name: 'Store 1' })
const store2 = store({ get name () { return store1.name } })
全球自动效果
全球自动效应可创建于 autoEffect 并清理出来 clearEffect。 。 。 .
import { store, autoEffect, clearEffect } from '@risingstack/react-easy-state';
const app = store({ name: 'My App' })
const effect = autoEffect(() => document.title = app.name)
// this also updates the document title
app.name = 'My Awesome App'
clearEffect(effect)
// this won't update the document title, the effect is cleared
app.name = 'My App'
函数组件中的本地自动效果
在函数组件中使用本地自动效果而不是 useEffect 当它们内部使用活性储存时会用到钩子. 当组件卸载时,这些局部效应会自动清除.
import React from 'react'
import { store, view, autoEffect } from '@risingstack/react-easy-state';
export default view(() => {
const app = store({ name: 'My App' })
// no need to clear the effect
autoEffect(() => document.title = app.name)
})
明确没有像香草道具和状态这样的被动依赖性通过函数组件中的本地自动效应.
由于React hooks的设计,你必须明确将所有无反应数据传递给钩状依赖阵列. 这使得当无反应数据发生变化时,效果也能运行.
import React from 'react'
import { store, view, autoEffect } from '@risingstack/react-easy-state';
export default view(({ greeting }) => {
const app = store({ name: 'My App' })
// pass `greeting` in the dependency array because it is not coming from a store
autoEffect(() => document.title = `${greeting} ${app.name}`, [greeting])
})
类组件中的本地自动效果
当组件卸载时,必须清除类组件中的局部效应.
import React, { Component } from 'react'
import { store, view, autoEffect } from '@risingstack/react-easy-state';
class App extends Component {
app = store({ name: 'My App' })
componentDidMount () {
this.effect = autoEffect(() => document.title = this.app.name)
}
componentWillUnmount () {
// local effects in class components must be cleared on unmount
clearEffect(this.effect)
}
}
API 摘要:书:
存储( obj)
从通过的对象创建可观察到的存储并返回。 可用于外部组件 全球库存 和内部组件 本地仓库。 。 。 .
import { store } from '@risingstack/react-easy-state';
const user = store({ name: 'Rick' });
视图( CAP)
创建一个 被动视图 从经过的组件返回。 反应视图在它内部使用的任何存储数据被变异时都会重新发送.
import React from 'react';
import { view, store } from '@risingstack/react-easy-state';
const user = store({ name: 'Bob' });
export default view(() => (
<div>Hello {user.name}!</div>
));
批次(fn)
立即执行已传递的函数,并批量其内的所有存储突变. 断接突变保证不会引发不必要的双重成份。 大多数任务源都是自动批量的,只使用 batch 如果你遇到表现问题.
import React from 'react';
import { view, store } from '@risingstack/react-easy-state';
const user = store({ name: 'Bob' });
function setName() {
batch(() => {
user.name = 'Rick'
user.name = 'Ann'
})
}
自动效果( fn)
从经过的函数创建反应函数,立即执行,并返回. 一个反应函数在它内部使用的任何存储数据被变异时都会自动重放.
import { store, autoEffect } from '@risingstack/react-easy-state';
const user = store({ name: 'Bob' })
autoEffect(() => document.title = user.name)
明显效果( fn)
使用反应函数(返回于 autoEffect)并清除其反应. 清除后的反应功能将不再在相关商店突变上重新运行. 函数组件内创建的活性函数在组件卸载时自动清除.
import { store, autoEffect, clearEffect } from '@risingstack/react-easy-state';
const user = store({ name: 'Bob' })
const effect = autoEffect(() => document.title = user.name)
clearEffect(effect)
有活演示的例子 : tv:
初学者
- 时钟部件 (单位:千美元)来源(中文(简体) )代码和框(中文(简体) )反应内源(中文(简体) )反应-内生沙盒:可重复使用的时钟元件,并有小型的本地国家商店.
- 停止监视 (单位:千美元)来源(中文(简体) )代码和框(中文(简体) )教程:一种由常态和更接地态属性混合而成的停止表.
高级
- 波克德克斯语Name (单位:千美元)来源(中文(简体) )代码和框: a Pokédex app 与阿波罗GraphQL相接而成,有Aync动作和全球状态.
- 图多MVC (单位:千美元)来源(中文(简体) )代码和框:是一款经典的TodoMVC执行,具有大量的取取取器/取取取取取取取取取取取取并隐含回放.
- 联系人表 (单位:千美元)来源(中文(简体) )代码和框: 数据网的执行由全球和地方状态混合而成.
- 啤酒搜索器 (单位:千美元)来源(中文(简体) )代码和框(中文(简体) )教程:一个带有同声动作的应用程序,以及本地和全球状态的组合,它为您找到相配的啤酒.
文章:发音:
- 引入反应易态:制作一个简单的停站表.
- 压力测试反应轻松状态: 展示Easy State对越来越异国情调的反动.
- 具有反应易状态的设计模式:通过啤酒发现器应用演示协同动作以及地方和全球状态管理.
- 反应简单状态背后的想法: Easy State的引擎盖下深潜.
性能:火箭:
您可以将 Easy State 与 Plain React 等州管理图书馆进行比较,其基准如下: 它的性能比"MobX"好一点,和"Redux"类似.
平台支持: 计算机:
- 节点:6及以上
- 铬:49及以上
- 火狐:38及以上
- 萨法里:10及以上
- 边缘:12及以上
- 歌剧院:36及以上
- 土著反应: 0.59和以上
这个库基于不可多填的ES6代理. 由于这个原因,它永远不会支持IE.
替代结构:扳手:
此库检测是否使用ES6或普通JS模块,并为您提供正确的格式. 默认捆绑使用ES6特性,这些特性可能尚未被一些变更工具所支持. 如果在构建过程中遇到问题,可以从下面切换到ES5构建中的一种.
@risingstack/react-easy-state/dist/es.es6.js曝光一个带有ES6模块的ES6架构.@risingstack/react-easy-state/dist/es.es5.js暴露一个ES5的建筑,并配有ES6模块.@risingstack/react-easy-state/dist/cjs.es6.js曝光一个带有通用JS模块的ES6构建.@risingstack/react-easy-state/dist/cjs.es5.js曝光一个ES5架构并有通用JS模块.
如果使用捆绑器,则设置一个别名 @risingstack/react-easy-state 指向你们想要的建筑 你可以学会如何用网络包装 这里 并随卷而出 这里。 。 。 .
贡献者: sparkles :
总是欢迎捐款,请读我们 提供文档。 。 。 .
感谢这些伟大的人emoji 密钥:
米克洛斯·贝尔塔兰 💻 ⚠️ 📖 📝 |
罗兰 💻 ⚠️ 📖 |
丹尼尔·格吉利 💻 🎨 💡 |
彼得·齐比克 🚇 |
本项目遵循 全部贡献者 规格。 任何捐款都欢迎!
