如何在相关的react组件之间进行通信?

我刚开始使用ReactJS,我有一个问题。

我的应用程序本质上是一个带有过滤器和按钮来更改布局的列表。 目前我正在使用三个组件:<list />< Filters /><TopBar />,现在显然,当我在< Filters />中更改设置时,我想在<list />中触发一些方法来更新我的视图

我如何使这3个组件相互交互,或者我是否需要某种全局数据模型,我只需要对其进行更改?

212464 次浏览

最好的方法取决于您计划如何安排这些组件。下面是我现在想到的几个例子:

  1. <Filters /><List />的子组件
  2. <Filters /><List />都是父组件的子组件
  3. <Filters /><List />完全存在于独立的根组件中。

可能还有其他我没有想到的情况。如果你的不符合这些要求,请告诉我。以下是我处理前两种情况的一些非常粗略的例子:

场景# 1

你可以将一个处理程序从<List />传递给<Filters />,然后在onChange事件上调用该处理程序,用当前值筛选列表。

JSFiddle for #1→

/** @jsx React.DOM */


var Filters = React.createClass({
handleFilterChange: function() {
var value = this.refs.filterInput.getDOMNode().value;
this.props.updateFilter(value);
},
render: function() {
return <input type="text" ref="filterInput" onChange={this.handleFilterChange} placeholder="Filter" />;
}
});


var List = React.createClass({
getInitialState: function() {
return {
listItems: ['Chicago', 'New York', 'Tokyo', 'London', 'San Francisco', 'Amsterdam', 'Hong Kong'],
nameFilter: ''
};
},
handleFilterUpdate: function(filterValue) {
this.setState({
nameFilter: filterValue
});
},
render: function() {
var displayedItems = this.state.listItems.filter(function(item) {
var match = item.toLowerCase().indexOf(this.state.nameFilter.toLowerCase());
return (match !== -1);
}.bind(this));


var content;
if (displayedItems.length > 0) {
var items = displayedItems.map(function(item) {
return <li>{item}</li>;
});
content = <ul>{items}</ul>
} else {
content = <p>No items matching this filter</p>;
}


return (
<div>
<Filters updateFilter={this.handleFilterUpdate} />
<h4>Results</h4>
{content}
</div>
);
}
});


React.renderComponent(<List />, document.body);

场景# 2

与场景#1类似,但父组件将是将处理程序函数传递给<Filters />的组件,并将过滤后的列表传递给<List />。我更喜欢这个方法,因为它解耦了<List /><Filters />

JSFiddle for #2→

/** @jsx React.DOM */


var Filters = React.createClass({
handleFilterChange: function() {
var value = this.refs.filterInput.getDOMNode().value;
this.props.updateFilter(value);
},
render: function() {
return <input type="text" ref="filterInput" onChange={this.handleFilterChange} placeholder="Filter" />;
}
});


var List = React.createClass({
render: function() {
var content;
if (this.props.items.length > 0) {
var items = this.props.items.map(function(item) {
return <li>{item}</li>;
});
content = <ul>{items}</ul>
} else {
content = <p>No items matching this filter</p>;
}
return (
<div className="results">
<h4>Results</h4>
{content}
</div>
);
}
});


var ListContainer = React.createClass({
getInitialState: function() {
return {
listItems: ['Chicago', 'New York', 'Tokyo', 'London', 'San Francisco', 'Amsterdam', 'Hong Kong'],
nameFilter: ''
};
},
handleFilterUpdate: function(filterValue) {
this.setState({
nameFilter: filterValue
});
},
render: function() {
var displayedItems = this.state.listItems.filter(function(item) {
var match = item.toLowerCase().indexOf(this.state.nameFilter.toLowerCase());
return (match !== -1);
}.bind(this));


return (
<div>
<Filters updateFilter={this.handleFilterUpdate} />
<List items={displayedItems} />
</div>
);
}
});


React.renderComponent(<ListContainer />, document.body);

场景# 3

当组件不能在任何类型的父子关系之间进行通信时,文档建议设置一个全局事件系统. 文档建议设置一个全局事件系统. 文档建议设置一个全局事件系统

这是我处理这个的方式 假设你有一个<select>for and a <一天。 天数取决于选择的月份

两个列表都属于第三个对象,即左面板。& lt; select>也是leftPanel <div>
这是一个带有LeftPanel组件中的回调和处理程序的游戏

要测试它,只需将代码复制到两个分开的文件中并运行index . html. exe。然后选择一个月,看看天数的变化情况。

dates.js

    /** @jsx React.DOM */




var monthsLength = [0,31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
var MONTHS_ARR = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];


var DayNumber = React.createClass({
render: function() {
return (
<option value={this.props.dayNum}>{this.props.dayNum}</option>
);
}
});


var DaysList = React.createClass({
getInitialState: function() {
return {numOfDays: 30};
},
handleMonthUpdate: function(newMonthix) {
this.state.numOfDays = monthsLength[newMonthix];
console.log("Setting days to " + monthsLength[newMonthix] + " month = " + newMonthix);


this.forceUpdate();
},
handleDaySelection: function(evt) {
this.props.dateHandler(evt.target.value);
},
componentDidMount: function() {
this.props.readyCallback(this.handleMonthUpdate)
},
render: function() {
var dayNodes = [];
for (i = 1; i <= this.state.numOfDays; i++) {
dayNodes = dayNodes.concat([<DayNumber dayNum={i} />]);
}
return (
<select id={this.props.id} onChange = {this.handleDaySelection}>
<option value="" disabled defaultValue>Day</option>
{dayNodes}
</select>
);
}
});


var Month = React.createClass({
render: function() {
return (
<option value={this.props.monthIx}>{this.props.month}</option>
);
}
});


var MonthsList = React.createClass({
handleUpdate: function(evt) {
console.log("Local handler:" + this.props.id + " VAL= " + evt.target.value);
this.props.dateHandler(evt.target.value);


return false;
},
render: function() {
var monthIx = 0;


var monthNodes = this.props.data.map(function (month) {
monthIx++;
return (
<Month month={month} monthIx={monthIx} />
);
});


return (
<select id = {this.props.id} onChange = {this.handleUpdate}>
<option value="" disabled defaultValue>Month</option>
{monthNodes}
</select>
);
}
});


var LeftPanel = React.createClass({
dayRefresh: function(newMonth) {
// Nothing - will be replaced
},
daysReady: function(refreshCallback) {
console.log("Regisering days list");
this.dayRefresh = refreshCallback;
},
handleMonthChange: function(monthIx) {
console.log("New month");
this.dayRefresh(monthIx);
},
handleDayChange: function(dayIx) {
console.log("New DAY: " + dayIx);
},
render: function() {
return(
<div id="orderDetails">
<DaysList id="dayPicker" dateHandler={this.handleDayChange} readyCallback = {this.daysReady} />
<MonthsList data={MONTHS_ARR} id="monthPicker" dateHandler={this.handleMonthChange}  />
</div>
);
}
});






React.renderComponent(
<LeftPanel />,
document.getElementById('leftPanel')
);
和运行左面板组件的HTML index . html < / >强

<!DOCTYPE html>
<html>
<head>
<title>Dates</title>


<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js"></script>
<script src="//fb.me/react-0.11.1.js"></script>
<script src="//fb.me/JSXTransformer-0.11.1.js"></script>
</head>


<style>


#dayPicker {
position: relative;
top: 97px;
left: 20px;
width: 60px;
height: 17px;
}


#monthPicker {
position: relative;
top: 97px;
left: 22px;
width: 95px;
height: 17px;
}


select {
font-size: 11px;
}


</style>




<body>
<div id="leftPanel">
</div>


<script type="text/jsx" src="dates.js"></script>


</body>
</html>

有多种方法可以使组件进行通信。有些可能适合您的用例。下面是一些我认为有用的知识。

反应

亲子直接交流

const Child = ({fromChildToParentCallback}) => (
<div onClick={() => fromChildToParentCallback(42)}>
Click me
</div>
);


class Parent extends React.Component {
receiveChildValue = (value) => {
console.log("Parent received value from child: " + value); // value is 42
};
render() {
return (
<Child fromChildToParentCallback={this.receiveChildValue}/>
)
}
}

在这里,子组件将调用父组件提供的带有值的回调,父组件将能够获得父组件中子组件提供的值。

如果你为你的应用程序构建一个功能/页面,最好有一个单一的父类来管理回调/状态(也称为containersmart component),并且所有的子类都是无状态的,只向父类报告事情。通过这种方式,您可以轻松地将父进程的状态“共享”给任何需要它的子进程。


上下文

React Context允许将状态保存在组件层次结构的根,并且能够很容易地将这种状态注入嵌套非常深的组件中,而不必麻烦地将道具传递给每个中间组件。

到目前为止,上下文还只是一个实验性的特性,但在React 16.3中有了一个新的API。

const AppContext = React.createContext(null)


class App extends React.Component {
render() {
return (
<AppContext.Provider value=\{\{language: "en",userId: 42}}>
<div>
...
<SomeDeeplyNestedComponent/>
...
</div>
</AppContext.Provider>
)
}
};


const SomeDeeplyNestedComponent = () => (
<AppContext.Consumer>
{({language}) => <div>App language is currently {language}</div>}
</AppContext.Consumer>
);

使用者正在使用呈现道具/子函数模式

查看博客获取更多细节。

在React 16.3之前,我建议使用react-broadcast,它提供了非常相似的API,并使用以前的上下文API。


门户网站

当你想让两个组件靠得很近,让它们用简单的函数通信时,使用门户,就像正常的父/子组件一样,但你不希望这两个组件在DOM中有父/子关系,因为它暗示了视觉/ CSS约束(如z-index,不透明度……)

在这种情况下,您可以使用“门户”。有不同的react库使用门户网站,通常用于情态动词,弹出窗口,工具提示…

考虑以下几点:

<div className="a">
a content
<Portal target="body">
<div className="b">
b content
</div>
</Portal>
</div>

reactAppContainer中呈现时可以产生以下DOM:

<body>
<div id="reactAppContainer">
<div className="a">
a content
</div>
</div>
<div className="b">
b content
</div>
</body>

更多细节在这里 .


在某处定义一个槽,然后从呈现树的另一个位置填充该槽。

import { Slot, Fill } from 'react-slot-fill';


const Toolbar = (props) =>
<div>
<Slot name="ToolbarContent" />
</div>


export default Toolbar;


export const FillToolbar = ({children}) =>
<Fill name="ToolbarContent">
{children}
</Fill>

这有点类似于门户,除了填充的内容将呈现在您定义的槽中,而门户通常呈现一个新的dom节点(通常是document.body的子节点)

检查react-slot-fill


事件总线

正如React 文档中所述:

对于没有父子关系的两个组件之间的通信,您可以设置自己的全局事件系统。在componentDidMount()中订阅事件,在componentWillUnmount()中取消订阅,并在收到事件时调用setState()。

可以使用许多方法来设置事件总线。您可以只创建一个侦听器数组,在事件发布时,所有侦听器都将收到该事件。或者你可以使用EventEmitterPostalJs


通量

通量基本上是一个事件总线,除了事件接收器是存储。这类似于基本的事件总线系统,只是状态是在React之外管理的

最初的Flux实现看起来像是试图以一种俗套的方式来做事件源。

回来的对我来说是最接近事件源的Flux实现,这得益于许多事件源的优势,比如时间旅行的能力。它没有严格地链接到React,也可以与其他函数视图库一起使用。

Egghead的Redux 视频教程非常好,并解释了它的内部工作原理(它真的很简单)。


游标

游标来自ClojureScript / Om,在React项目中广泛使用。它们允许在React之外管理状态,并允许多个组件对状态的同一部分进行读/写访问,而不需要了解任何关于组件树的信息。

存在许多实现,包括ImmutableJSReact-cursors无所不知的

编辑2016:似乎人们同意游标工作在较小的应用程序,但它不能很好地扩展复杂的应用程序。Om Next不再有游标(虽然最初是Om引入了这个概念)


榆树架构

榆树架构是由榆树语言使用的体系结构。即使Elm不是ReactJS,也可以在React中完成Elm架构。

Redux的作者Dan Abramov使用React对Elm架构做了一个实现

Redux和Elm都很棒,都倾向于在前端支持事件源概念,都允许时间旅行调试、撤销/重做、重放……

Redux和Elm之间的主要区别是Elm倾向于对状态管理更加严格。在Elm中,你不能有本地组件状态或挂载/卸载钩子,所有DOM更改必须由全局状态更改触发。Elm体系结构提出了一种可扩展的方法,允许在单个不可变对象中处理所有状态,而Redux提出了一种方法,邀请您在单个不可变对象中处理状态的大多数

虽然Elm的概念模型非常优雅,其架构允许在大型应用程序上很好地扩展,但在实践中,实现简单的任务可能会很困难,或者涉及更多的样板文件,比如在安装输入后将焦点放在输入上,或者与现有的库集成,使用强制接口(如JQuery插件)。相关问题

此外,Elm体系结构涉及更多的代码样板。写起来并没有那么冗长或复杂,但我认为Elm架构更适合静态类型语言。


玻璃钢

像RxJS, BaconJS或Kefir这样的库可以用来生成FRP流来处理组件之间的通信。

例如,你可以尝试Rx-React

我认为使用这些库与使用ELM语言提供的信号非常相似。

CycleJS框架不使用ReactJS,而是使用vdom。它与Elm架构有很多相似之处(但在现实生活中更容易使用,因为它允许vdom钩子),它广泛使用RxJs而不是函数,如果你想在React中使用FRP,它可以是一个很好的灵感来源。CycleJs Egghead视频是很好的理解它是如何工作的。


CSP

CSP(通信顺序进程)目前很流行(主要是因为Go/goroutines和core.async/ClojureScript),但你也可以在javascript中使用JS-CSP

James Long已经做了一个视频来解释它如何与React一起使用。

传奇

saga是来自DDD / EventSourcing / CQRS世界的后端概念,也称为“进程管理器”。 它是由redux-saga项目推广的,主要是作为redux-thunk处理副作用(即API调用等)的替代品。目前大多数人认为它只服务于副作用,但实际上它更多的是关于解耦组件。< / p >

它更像是对Flux架构(或Redux)的赞美,而不是一个全新的通信系统,因为传奇在最后会发出Flux动作。其思想是,如果您有widget1和widget2,并且希望它们解耦,则不能从widget1触发针对widget2的操作。因此,您让widget1只触发针对自己的动作,而saga是一个“后台进程”,它侦听widget1的动作,并可能分派针对widget2的动作。saga是两个小部件之间的耦合点,但小部件仍然是解耦的。

如果你感兴趣,可以看看我的答案是


结论

如果你想查看相同小应用程序使用这些不同样式的示例,请检查存储库的分支。

我不知道从长远来看什么是最好的选择,但我真的很喜欢Flux看起来像事件源的样子。

如果你不知道事件源的概念,看看这个非常有教学意义的博客:使用apache Samza翻转数据库,这是一个必读的理解为什么Flux很好(但这也适用于FRP)

我认为社区同意最有前途的Flux实现是回来的,由于热重新加载,它将逐步允许非常富有成效的开发人员体验。令人印象深刻的现场编码ala Bret Victor的原理发明视频是可能的!

即使他们不是父母-孩子关系,也有这样的可能性——那就是流动。有一个很好的(对我个人来说)实现称为Alt.JS(与Alt-Container)。

例如,您可以拥有依赖于组件详细信息设置的边栏。Component Sidebar与SidebarActions和SidebarStore连接,而Details是DetailsActions和DetailsStore。

你可以像那样使用AltContainer

<AltContainer stores=\{\{
SidebarStore: SidebarStore
}}>
<Sidebar/>
</AltContainer>


{this.props.content}

这将保留stores(好吧,我可以使用“store”而不是“stores”道具)。现在,{this.props。content}可以是细节,取决于路由。假设/Details将我们重定向到那个视图。 例如,详细信息将有一个复选框,如果选中的话,将把Sidebar元素从X更改为Y

从技术上讲,它们之间没有关系,没有通量是很难做到的。但这样就相当容易了。

现在让我们进入DetailsActions。我们会在那里创建

class SiteActions {
constructor() {
this.generateActions(
'setSiteComponentStore'
);
}


setSiteComponent(value) {
this.dispatch({value: value});
}
}

和DetailsStore

class SiteStore {
constructor() {
this.siteComponents = {
Prop: true
};


this.bindListeners({
setSiteComponent: SidebarActions.COMPONENT_STATUS_CHANGED
})
}


setSiteComponent(data) {
this.siteComponents.Prop = data.value;
}
}

现在,这就是魔法开始的地方。

如你所见,这里有SidebarActions的bindListener。如果使用setSiteComponent,将使用ComponentStatusChanged。

现在在SidebarActions

    componentStatusChanged(value){
this.dispatch({value: value});
}

我们有这样的东西。它会在调用时分派那个对象。如果setSiteComponent in store将被使用(你可以在组件中使用例如onChange on Button或其他)它将被调用

现在在边栏商店中我们会有

    constructor() {
this.structures = [];


this.bindListeners({
componentStatusChanged: SidebarActions.COMPONENT_STATUS_CHANGED
})
}


componentStatusChanged(data) {
this.waitFor(DetailsStore);


_.findWhere(this.structures[0].elem, {title: 'Example'}).enabled = data.value;
}

这里你可以看到,它会等待DetailsStore。这是什么意思?这或多或少意味着这个方法需要等待DetailsStoreto更新才能更新自己。

< p > tl,博士 一个Store正在监听一个Store中的方法,并将从组件action中触发一个动作,该动作将更新它自己的Store

我希望它能对你有所帮助。

如果你想探索组件之间通信的选项,并且感觉它越来越难,那么你可以考虑采用一个好的设计模式:通量

它只是一组规则,定义了如何存储和更改应用程序范围内的状态,以及如何使用该状态来呈现组件。

有许多Flux实现,Facebook的正式实施是其中之一。虽然它被认为包含了最多的样板代码,但它更容易理解,因为大部分内容都是显式的。

其他选项有出现错误 fluxxor fluxible回来的

扩展@MichaelLaCroix的回答,当组件不能在任何类型的父子关系之间进行通信时,文档建议设置一个全局事件系统。

<Filters /><TopBar />没有任何上述关系的情况下,一个简单的全局发射器可以像这样使用:

componentDidMount -订阅事件

componentWillUnmount -取消订阅事件

React.js和EventSystem code .js

EventSystem.js

class EventSystem{


constructor() {
this.queue = {};
this.maxNamespaceSize = 50;
}


publish(/** namespace **/ /** arguments **/) {
if(arguments.length < 1) {
throw "Invalid namespace to publish";
}


var namespace = arguments[0];
var queue = this.queue[namespace];


if (typeof queue === 'undefined' || queue.length < 1) {
console.log('did not find queue for %s', namespace);
return false;
}


var valueArgs = Array.prototype.slice.call(arguments);


valueArgs.shift(); // remove namespace value from value args


queue.forEach(function(callback) {
callback.apply(null, valueArgs);
});


return true;
}


subscribe(/** namespace **/ /** callback **/) {
const namespace = arguments[0];
if(!namespace) throw "Invalid namespace";
const callback = arguments[arguments.length - 1];
if(typeof callback !== 'function') throw "Invalid callback method";


if (typeof this.queue[namespace] === 'undefined') {
this.queue[namespace] = [];
}


const queue = this.queue[namespace];
if(queue.length === this.maxNamespaceSize) {
console.warn('Shifting first element in queue: `%s` since it reached max namespace queue count : %d', namespace, this.maxNamespaceSize);
queue.shift();
}


// Check if this callback already exists for this namespace
for(var i = 0; i < queue.length; i++) {
if(queue[i] === callback) {
throw ("The exact same callback exists on this namespace: " + namespace);
}
}


this.queue[namespace].push(callback);


return [namespace, callback];
}


unsubscribe(/** array or topic, method **/) {
let namespace;
let callback;
if(arguments.length === 1) {
let arg = arguments[0];
if(!arg || !Array.isArray(arg)) throw "Unsubscribe argument must be an array";
namespace = arg[0];
callback = arg[1];
}
else if(arguments.length === 2) {
namespace = arguments[0];
callback = arguments[1];
}


if(!namespace || typeof callback !== 'function') throw "Namespace must exist or callback must be a function";
const queue = this.queue[namespace];
if(queue) {
for(var i = 0; i < queue.length; i++) {
if(queue[i] === callback) {
queue.splice(i, 1); // only unique callbacks can be pushed to same namespace queue
return;
}
}
}
}


setNamespaceSize(size) {
if(!this.isNumber(size)) throw "Queue size must be a number";
this.maxNamespaceSize = size;
return true;
}


isNumber(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}


}

NotificationComponent.js

class NotificationComponent extends React.Component {


getInitialState() {
return {
// optional. see alternative below
subscriber: null
};
}


errorHandler() {
const topic = arguments[0];
const label = arguments[1];
console.log('Topic %s label %s', topic, label);
}


componentDidMount() {
var subscriber = EventSystem.subscribe('error.http', this.errorHandler);
this.state.subscriber = subscriber;
}


componentWillUnmount() {
EventSystem.unsubscribe('error.http', this.errorHandler);


// alternatively
// EventSystem.unsubscribe(this.state.subscriber);
}


render() {


}
}

我看到这个问题已经回答了,但如果你想了解更多细节,总共有组件间通信3例:

  • 案例1:父到子通信
  • 案例2:子到父通信
  • 案例3:不相关的组件(任何组件到任何组件)通信

我曾经是你现在的位置,作为一个初学者,你有时会觉得不适应如何反应的方式来做这件事。我将尝试用我现在思考的方式来解决这个问题。

国家是交流的基石

通常它归结于你改变这个组件的状态的方式在你的例子中你指出三个组件。

<List />:它可能会根据过滤器显示一个项列表 <Filters />:过滤选项,将改变你的数据。 <TopBar />:选项列表

为了协调所有这些交互你需要一个更高的组件我们叫它App,它会把动作和数据传递给每个组件例如,它看起来是这样的

<div>
<List items={this.state.filteredItems}/>
<Filter filter={this.state.filter} setFilter={setFilter}/>
</div>

因此,当setFilter被调用时,它将影响filteredItem并重新渲染两个组件;。如果这不是完全清楚,我给你一个例子,你可以在一个文件中检查复选框:

import React, {Component} from 'react';
import {render} from 'react-dom';


const Person  = ({person, setForDelete}) => (
<div>
<input type="checkbox" name="person" checked={person.checked} onChange={setForDelete.bind(this, person)} />
{person.name}
</div>
);




class PeopleList extends Component {


render() {


return(
<div>
{this.props.people.map((person, i) => {
return <Person key={i} person={person} setForDelete={this.props.setForDelete} />;
})}
<div onClick={this.props.deleteRecords}>Delete Selected Records</div>
</div>
);
}


} // end class


class App extends React.Component {


constructor(props) {
super(props)
this.state = {people:[{id:1, name:'Cesar', checked:false},{id:2, name:'Jose', checked:false},{id:3, name:'Marbel', checked:false}]}
}


deleteRecords() {
const people = this.state.people.filter(p => !p.checked);


this.setState({people});
}


setForDelete(person) {
const checked = !person.checked;
const people = this.state.people.map((p)=>{
if(p.id === person.id)
return {name:person.name, checked};
return p;
});


this.setState({people});
}


render () {


return <PeopleList people={this.state.people} deleteRecords={this.deleteRecords.bind(this)} setForDelete={this.setForDelete.bind(this)}/>;
}
}


render(<App/>, document.getElementById('app'));
下面的代码帮助我建立两个兄弟姐妹之间的通信。设置是在render()和componentDidMount()调用期间在它们的父节点中完成的。 它基于https://reactjs.org/docs/refs-and-the-dom.html

class App extends React.Component<IAppProps, IAppState> {
private _navigationPanel: NavigationPanel;
private _mapPanel: MapPanel;


constructor() {
super();
this.state = {};
}


// `componentDidMount()` is called by ReactJS after `render()`
componentDidMount() {
// Pass _mapPanel to _navigationPanel
// It will allow _navigationPanel to call _mapPanel directly
this._navigationPanel.setMapPanel(this._mapPanel);
}


render() {
return (
<div id="appDiv" style={divStyle}>
// `ref=` helps to get reference to a child during rendering
<NavigationPanel ref={(child) => { this._navigationPanel = child; }} />
<MapPanel ref={(child) => { this._mapPanel = child; }} />
</div>
);
}
}

好吧,有很少的方法来做到这一点,但我只想专注于使用商店使用回来的,这让你的生活更容易在这些情况下,而不是给你一个快速的解决方案,只在这种情况下,使用纯React将在真正的大应用程序中结束混乱和组件之间的通信变得越来越难,随着应用程序的增长…

那么回来的为你做什么呢?

Redux就像应用程序中的本地存储,当你需要在应用程序的不同位置使用数据时,可以使用它。

基本上,Redux的想法最初来自于flux,但有一些根本性的变化,包括通过只创建一个存储来拥有一个真相来源的概念……

请看下面的图表,看看通量回来的之间的一些差异…

Redux and Flux

如果您的应用程序需要组件之间的通信,请考虑从一开始就在应用程序中应用回来的

另外,阅读Redux文档中的这些词可能会有帮助:

作为JavaScript单页应用程序的要求 变得越来越复杂,我们的代码必须管理比 以往> < /强。这种状态包括服务器响应和缓存数据, 以及尚未持久化到的本地创建的数据 服务器。UI state的复杂度也在增加,这是我们需要的 管理活动路由,选定的选项卡,旋转器,分页控件,

管理这种不断变化的状态是困难的。如果一个模型可以更新 另一个模型,然后视图可以更新一个模型,该模型更新另一个模型 模型,而这反过来可能导致另一个视图更新。在一些 Point,你不再像以前那样了解应用中发生了什么 失去对其状态的时间、原因和方式的控制。当一个系统 是不透明的和不确定的,很难重现错误或添加 新特性。< / p > 如果这还不够糟糕,考虑一下新的要求 常见于前端产品开发。作为开发者,我们确实如此 期望处理乐观更新,服务器端渲染,抓取 执行路由转换前的数据,等等。我们发现自己 试图处理我们从未处理过的复杂问题 在此之前,我们不可避免地会问这样一个问题:是时候放弃了吗?的

这种复杂性很难处理,因为我们混合了两个概念 这对人类大脑来说是很难推理的:突变和 异步性。我叫它们曼妥思和可乐。两者都可以很好 分离,但聚在一起就会一团糟。像React这样的库 尝试在视图层中通过删除两者来解决这个问题 异步和直接DOM操作。的状态 你的数据由你自己决定。这是Redux进入的地方

按照通量、CQRS和事件源的步骤,Redux 试图通过强加某些 更新发生的方式和时间限制。这些限制 都反映在Redux的三个原则中。

奇怪的是,没有人提到mobx。其思想类似于redux。如果我有一段数据,多个组件订阅了它,那么我可以使用该数据来驱动多个组件。