ReactJS两个组件进行通信

本文探讨了ReactJS中组件间的通信方法,包括亲子直接通信、使用Context、Portals、Event bus、Flux等。文章通过场景示例介绍了如何在不同关系的组件间传递数据,同时提到了各种通信方式的适用场景,为ReactJS开发提供了通信策略指导。
摘要由CSDN通过智能技术生成

本文翻译自:ReactJS Two components communicating

I just got started with ReactJS and am a little stuck on a problem that I have. 我刚开始使用ReactJS,但对我遇到的问题有些困惑。

My application is essentially a list with filters and a button to change the layout. 我的应用程序本质上是一个带有过滤器的列表和一个用于更改布局的按钮。 At the moment I'm using three components: <list /> , < Filters /> and <TopBar /> , now obviously when I change settings in < Filters /> I want to trigger some method in <list /> to update my view. 目前,我正在使用三个组件: <list />< Filters /><TopBar /> ,现在很明显,当我更改< Filters />设置时,我想触发<list />某些方法来更新我的视图。

How can I make those 3 components interact with each other, or do I need some sort of global data model where I can just make changes to? 如何使这三个组件相互交互,或者我需要可以对其进行更改的某种全局数据模型?


#1楼

参考:https://stackoom.com/question/1RJRL/ReactJS两个组件进行通信


#2楼

The best approach would depend on how you plan to arrange those components. 最佳方法取决于您计划如何安排这些组件。 A few example scenarios that come to mind right now: 现在想到的一些示例场景:

  1. <Filters /> is a child component of <List /> <Filters /><List />的子组件
  2. Both <Filters /> and <List /> are children of a parent component <Filters /><List />都是父组件的子代
  3. <Filters /> and <List /> live in separate root components entirely. <Filters /><List />完全位于单独的根组件中。

There may be other scenarios that I'm not thinking of. 可能还有其他我没有想到的情况。 If yours doesn't fit within these, then let me know. 如果您的不适合这些,请告诉我。 Here are some very rough examples of how I've been handling the first two scenarios: 以下是一些我如何处理前两种情况的非常粗糙的示例:

Scenario #1 场景1

You could pass a handler from <List /> to <Filters /> , which could then be called on the onChange event to filter the list with the current value. 您可以将处理程序从<List />传递给<Filters /> ,然后可以在onChange事件上调用该处理程序以使用当前值过滤列表。

JSFiddle for #1 → #1的JSFiddle→

/** @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);

Scenario #2 场景2

Similar to scenario #1, but the parent component will be the one passing down the handler function to <Filters /> , and will pass the filtered list to <List /> . 与方案1类似,但父组件将是将处理函数传递给<Filters /> ,并将过滤后的列表传递给<List /> I like this method better since it decouples the <List /> from the <Filters /> . 我更喜欢这种方法,因为它使<List /><Filters />

JSFiddle for #2 → #2的JSFiddle→

/** @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);

Scenario #3 场景#3

When the components can't communicate between any sort of parent-child relationship, the documentation recommends setting up a global event system . 当组件无法在任何类型的父子关系之间进行通信时, 文档建议设置全局事件系统


#3楼

This is the way I handled this. 这就是我处理这个问题的方式。
Let's say you have a <select> for Month and a <select> for Day . 假设您有一个<select>代表月份 ,一个<select>代表Day The number of days depends on the selected month. 天数取决于所选的月份。

Both lists are owned by a third object, the left panel. 这两个列表均由第三个对象(左侧面板)拥有。 Both <select> are also children of the leftPanel <div> 两者<select>也是leftPanel <div>的子元素
It's a game with the callbacks and the handlers in the LeftPanel component. 这是一个在LeftPanel组件中包含回调和处理程序的游戏

To test it, just copy the code into two separated files and run the index.html . 要对其进行测试,只需将代码复制到两个单独的文件中,然后运行index.html Then select a month and see how the number of days changes. 然后选择一个月,看看天数如何变化。

dates.js 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')
    );

And the HTML for running the left panel component index.html 以及用于运行左面板组件index.html的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>

#4楼

There are multiple ways to make components communicate. 有多种方法可以使组件进行通信。 Some can be suited to your usecase. 有些可以适合您的用例。 Here is a list of some I've found useful to know. 这是我发现有用的一些清单。

React 反应

Parent / Child direct communication 亲子直接沟通

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}/>
    )
  }
}

Here the child component will call a callback provided by the parent with a value, and the parent will be able to get the value provided by the children in the parent. 在这里,子组件将调用父组件提供的带有值的回调,并且父组件将能够获取父组件中子组件提供的值。

If you build a feature/page of your app, it's better to have a single parent managing the callbacks/state (also called container or smart component ), and all childs to be stateless, only reporting things to the parent. 如果您构建应用程序的功能/页面,则最好由一个父级来管理回调/状态(也称为containersmart component ),而所有子级都必须是无状态的,只向父级报告情况。 This way you can easily "share" the state of the parent to any child that need it. 这样,您可以轻松地将“父母”的状态“共享”给需要它的任何孩子。


Context 语境

React Context permits to hold state at the root of your component hierarchy, and be able to inject this state easily into very deeply nested components, without the hassle to have to pass down props to every intermediate components. React Context允许将状态保持在组件层次结构的根部,并且能够轻松地将此状态注入到嵌套很深的组件中,而不必麻烦将prop传递给每个中间组件。

Until now, context was an experimental feature, but a new API is available in React 16.3. 到目前为止,上下文是一个实验性功能,但是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>
);

The consumer is using the render prop / children function pattern 消费者正在使用render prop / children功能模式

Check this blog post for more details. 查看此博客文章以了解更多详细信息。

Before React 16.3, I'd recommend using react-broadcast which offer quite similar API, and use former context API. 在React 16.3之前,我建议使用提供完全相似的API的react-broadcast ,并使用以前的上下文API。


Portals 门户网站

Use a portal when you'd like to keep 2 components close together to make them communicate with simple functions, like in normal parent / child, but you don't want these 2 components to have a parent/child relationship in the DOM, because of visual / CSS constraints it implies (like z-index, opacity...). 当您想要将2个组件保持在一起以使它们与简单的功能进行通信时(如在正常的父/子中),请使用门户,但是您不希望这2个组件在DOM中具有父/子关系,因为暗示的视觉/ CSS约束(例如z-index,不透明度...)。

In this case you can use a "portal". 在这种情况下,您可以使用“门户”。 There are different react libraries using portals , usually used for modals , popups, tooltips... 有多种使用门户的反应库,通常用于模式 ,弹出窗口,工具提示...

Consider the following: 考虑以下:

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

Could produce the following DOM when rendered inside reactAppContainer : reactAppContainer内部呈现时可以产生以下DOM:

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

More details here 在这里更多细节


Slots 插槽

You define a slot somewhere, and then you fill the slot from another place of your render tree. 您在某处定义一个插槽,然后从渲染树的另一个位置填充该插槽。

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>

This is a bit similar to portals except the filled content will be rendered in a slot you define, while portals generally render a new dom node (often a children of document.body) 这与门户网站有点类似,除了填充的内容将在您定义的插槽中呈现,而门户网站通常呈现新的dom节点(通常是document.body的子代)。

Check react-slot-fill library 检查反应槽填充


Event bus 活动巴士

As stated in the React documentation : 如React 文档中所述

For communication between two components that don't have a parent-child relationship, you can set up your own global event system. 要在没有父子关系的两个组件之间进行通信,可以设置自己的全局事件系统。 Subscribe to events in componentDidMount(), unsubscribe in componentWillUnmount(), and call setState() when you receive an event. 订阅componentDidMount()中的事件,取消订阅componentWillUnmount()中的事件,并在收到事件时调用setState()。

There are many things you can use to setup an event bus. 您可以使用许多东西来设置事件总线。 You can just create an array of listeners, and on event publish, all listeners would receive the event. 您可以只创建一个侦听器数组,并且在事件发布时,所有侦听器都将接收该事件。 Or you can use something like EventEmitter or PostalJs 或者您可以使用EventEmitterPostalJs之类的东西


Flux 助焊剂

Flux is basically an event bus, except the event receivers are stores. Flux基本上是事件总线,除了事件接收者是商店。 This is similar to the basic event bus system except the state is managed outside of React 这类似于基本事件总线系统,只不过状态是在React外部进行管理的

Original Flux implementation looks like an attempt to do Event-sourcing in a hacky way. 原始的Flux实现看起来像是尝试以一种怪异的方式进行事件源。

Redux is for me the Flux implementation that is the closest from event-sourcing, an benefits many of event-sourcing advantages like the ability to time-travel. 对我来说, Redux是最接近事件源的Flux实现,它受益于许多事件源优势,例如时间旅行能力。 It is not strictly linked to React and can also be used with other functional view libraries. 它没有严格地链接到React,也可以与其他功能视图库一起使用。

Egghead's Redux video tutorial is really nice and explains how it works internally (it really is simple). Egghead的Redux 视频教程非常好,并解释了它如何在内部工作(这很简单)。


Cursors 游标

Cursors are coming from ClojureScript/Om and widely used in React projects. 游标来自ClojureScript / Om,并广泛用于React项目。 They permit to manage the state outside of React, and let multiple components have read/write access to the same part of the state, without needing to know anything about the component tree. 它们允许管理React外部的状态,并允许多个组件对状态的同一部分具有读/写访问权限,而无需了解组件树的任何知识。

Many implementations exists, including ImmutableJS , React-cursors and Omniscient 存在许多实现,包括ImmutableJSReact-cursorsOmniscient

Edit 2016 : it seems that people agree cursors work fine for smaller apps but it does not scale well on complex apps. 编辑2016年 :似乎人们同意游标在较小的应用程序上可以正常工作,但在复杂的应用程序上无法很好地扩展。 Om Next does not have cursors anymore (while it's Om that introduced the concept initially) Om Next不再具有游标(这是Om最初引入的概念)


Elm architecture 榆木建筑

The Elm architecture is an architecture proposed to be used by the Elm language . Elm体系结构Elm语言建议使用的体系结构。 Even if Elm is not ReactJS, the Elm architecture can be done in React as well. 即使Elm不是ReactJS,Elm架构也可以在React中完成。

Dan Abramov, the author of Redux, did an implementation of the Elm architecture using React. Redux的作者Dan Abramov使用React 实现了Elm架构的实现

Both Redux and Elm are really great and tend to empower event-sourcing concepts on the frontend, both allowing time-travel debugging, undo/redo, replay... Redux和Elm都非常出色,并且倾向于在前端启用事件源概念,都允许时间旅行调试,撤消/重做,重播...

The main difference between Redux and Elm is that Elm tend to be a lot more strict about state management. Redux和Elm之间的主要区别在于Elm在状态管理方面趋于更加严格。 In Elm you can't have local component state or mount/unmount hooks and all DOM changes must be triggered by global state changes. 在Elm中,您不能具有本地组件状态或挂接/卸载挂接,并且所有DOM更改都必须由全局状态更改触发。 Elm architecture propose a scalable approach that permits to handle ALL the state inside a single immutable object, while Redux propose an approach that invites you to handle MOST of the state in a single immutable object. Elm体系结构提出了一种可扩展的方法,该方法允许处理单个不可变对象内的所有状态,而Redux提供一种方法,邀请您在单个不可变对象中处理状态的MOST

While the conceptual model of Elm is very elegant and the architecture permits to scale well on large apps, it can in practice be difficult or involve more boilerplate to achieve simple tasks like giving focus to an input after mounting it, or integrating with an existing library with an imperative interface (ie JQuery plugin). 虽然Elm的概念模型非常优雅,并且该架构允许在大型应用程序上很好地扩展,但实际上它可能很困难或需要更多样板来完成简单的任务,例如在安装后将焦点集中在输入上或与现有库集成具有命令式界面(即JQuery插件)。 Related issue . 相关问题

Also, Elm architecture involves more code boilerplate. 而且,Elm体系结构涉及更多代码样板。 It's not that verbose or complicated to write but I think the Elm architecture is more suited to statically typed languages. 编写起来并不那么冗长或复杂,但是我认为Elm体系结构更适合于静态类型的语言。


FRP 玻璃钢

Libraries like RxJS, BaconJS or Kefir can be used to produce FRP streams to handle communication between components. 可以使用RxJS,BaconJS或Kefir之类的库来生成FRP流,以处理组件之间的通信。

You can try for example Rx-React 您可以尝试例如Rx反应

I think using these libs is quite similar to using what the ELM language offers with signals . 我认为使用这些库与使用ELM语言提供的信号非常相似。

CycleJS framework does not use ReactJS but uses vdom . CycleJS框架不使用ReactJS,而是使用vdom It share a lot of similarities with the Elm architecture (but is more easy to use in real life because it allows vdom hooks) and it uses RxJs extensively instead of functions, and can be a good source of inspiration if you want to use FRP with React. 它与Elm架构有很多相似之处(但在现实生活中更容易使用,因为它允许使用vdom挂钩),并且广泛使用RxJ而不是函数,如果您想将FRP与反应 CycleJs Egghead videos are nice to understand how it works. CycleJs Egghead视频很高兴了解其工作原理。


CSP CSP

CSP (Communicating Sequential Processes) are currently popular (mostly because of Go/goroutines and core.async/ClojureScript) but you can use them also in javascript with JS-CSP . CSP(通信顺序过程)当前很流行(主要是因为Go / goroutines和core.async / ClojureScript),但是您也可以在JS-CSP的 javascript中使用它们。

James Long has done a video explaining how it can be used with React. James Long制作了一段视频,解释了如何将其与React一起使用。

Sagas 萨加斯

A saga is a backend concept that comes from the DDD / EventSourcing / CQRS world, also called "process manager". 传奇是来自DDD / EventSourcing / CQRS世界的后端概念,也称为“流程管理器”。 It is being popularized by the redux-saga project, mostly as a replacement to redux-thunk for handling side-effects (ie API calls etc). 它正在由redux-saga项目推广,主要是作为redux-thunk的替代品,用于处理副作用(即API调用等)。 Most people currently think it only services for side-effects but it is actually more about decoupling components. 当前,大多数人认为它仅用于副作用,但实际上更多是关于去耦组件。

It is more of a compliment to a Flux architecture (or Redux) than a totally new communication system, because the saga emit Flux actions at the end. 它比起全新的通信系统更像是对Flux架构(或Redux)的补充,因为传奇最终会发出Flux动作。 The idea is that if you have widget1 and widget2, and you want them to be decoupled, you can't fire action targeting widget2 from widget1. 这样的想法是,如果您有widget1和widget2,并且希望将它们解耦,则无法从widget1触发针对widget2的操作。 So you make widget1 only fire actions that target itself, and the saga is a "background process" that listens for widget1 actions, and may dispatch actions that target widget2. 因此,您使widget1仅触发以自身为目标的动作,并且传奇事件是侦听widget1动作的“后台进程”,并且可以调度以widget2为目标的动作。 The saga is the coupling point between the 2 widgets but the widgets remain decoupled. 传奇是2个小部件之间的耦合点,但小部件保持解耦。

If you are interested take a look at my answer here 如果您有兴趣,请在这里查看我的答案


Conclusion 结论

If you want to see an example of the same little app using these different styles, check the branches of this repository . 如果要查看使用这些不同样式的同一小应用程序的示例,请检查此存储库的分支。

I don't know what is the best option in the long term but I really like how Flux looks like event-sourcing. 从长远来看,我不知道什么是最佳选择,但我真的很喜欢Flux看起来像事件源。

If you don't know event-sourcing concepts, take a look at this very pedagogic blog: Turning the database inside out with apache Samza , it is a must-read to understand why Flux is nice (but this could apply to FRP as well) 如果您不了解事件源的概念,请看一下这个非常有教育意义的博客: 用apache Samza将数据库内翻 ,这是理解Flux为什么不错的必读书(但这也适用于FRP) )

I think the community agrees that the most promising Flux implementation is Redux , which will progressively allow very productive developer experience thanks to hot reloading. 我认为社区同意最有希望的Flux实现是Redux ,这得益于热重装,它将逐步提供非常富有成效的开发人员体验。 Impressive livecoding ala Bret Victor's Inventing on Principle video is possible! 令人印象深刻的实时编码ala Bret Victor的Inventing on Principle视频是可能的!


#5楼

There is such possibility even if they are not Parent - Child relationship - and that's Flux. 即使他们不是父母-子女关系-也有这种可能性。 There is pretty good (for me personally) implementation for that called Alt.JS (with Alt-Container). 对于Alt.JS(使用Alt-Container),有一个非常好的(对我个人而言)实现。

For example you can have Sidebar that is dependent on what is set in component Details. 例如,您可以具有取决于组件详细信息中设置的补充工具栏。 Component Sidebar is connected with SidebarActions and SidebarStore, while Details is DetailsActions and DetailsStore. 组件Sidebar与SidebarActions和SidebarStore连接,而Details是DetailsActions和DetailsS​​tore。

You could use then AltContainer like that 您可以像这样使用AltContainer

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

{this.props.content}

Which would keep stores (well I could use "store" instead of "stores" prop). 这将保留商店(我可以使用“ store”代替“ stores”道具)。 Now, {this.props.content} CAN BE Details depending on the route. 现在,{this.props.content}可以是详细信息,具体取决于路线。 Lets say that /Details redirect us to that view. 可以说/ Details将我们重定向到该视图。 Details would have for example a checkbox that would change Sidebar element from X to Y if it would be checked. 例如,“详细信息”将具有一个复选框,如果将其选中,它将把补充工具栏元素从X更改为Y。

Technically there is no relationship between them and it would be hard to do without flux. 从技术上讲,它们之间没有关系,没有助焊剂就很难做到。 BUT WITH THAT it is rather easy. 但是,这很容易。

Now let's get to DetailsActions. 现在让我们转到DetailsActions。 We will create there 我们将在那里创造

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

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

and DetailsStore 和DetailsS​​tore

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

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

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

And now, this is the place where magic begin. 现在,这是魔术开始的地方。

As You can see there is bindListener to SidebarActions.ComponentStatusChanged which will be used IF setSiteComponent will be used. 如您所见,如果将使用setSiteComponent,将使用SideListenerActions.ComponentStatusChanged的bindListener。

now in SidebarActions 现在在SidebarActions中

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

We have such thing. 我们有这样的东西。 It will dispatch that object on call. 它将在调用时分派该对象。 And it will be called if setSiteComponent in store will be used (that you can use in component for example during onChange on Button ot whatever) 如果将使用商店中的setSiteComponent,它将被调用(例如,您可以在onChange上的Button或其他期间在组件中使用)

Now in SidebarStore we will have 现在在SidebarStore中,我们将拥有

    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;
}

Now here you can see, that it will wait for DetailsStore. 现在在这里您可以看到它将等待DetailsS​​tore。 What does it mean? 这是什么意思? more or less it means that this method need to wait for DetailsStoreto update before it can update itself. 或多或少意味着该方法需要先等待DetailsS​​tore更新,然后才能更新自身。

tl;dr One Store is listening on methods in a store, and will trigger an action from component action, which will update its own store. tl; dr一个存储正在侦听存储中的方法,并将触发组件操作中的操作,组件操作将更新其自己的存储。

I hope it can help you somehow. 我希望它可以以某种方式帮助您。


#6楼

If you want to explore options of communicating between components and feel like it is getting harder and harder, then you might consider adopting a good design pattern: Flux . 如果您想探索组件之间进行通信的选项,并且感觉越来越难了,那么您可以考虑采用一种良好的设计模式: Flux

It is simply a collection of rules that defines how you store and mutate application wide state, and use that state to render components. 它只是规则的集合,这些规则定义了如何存储和更改应用程序范围内的状态以及如何使用该状态来呈现组件。

There are many Flux implementations, and Facebook's official implementation is one of them. Flux实现有很多, Facebook的官方实现就是其中之一。 Although it is considered the one that contains most boilerplate code, but it is easier to understand since most of the things are explicit. 尽管它被认为是包含大多数样板代码的代码,但是由于大多数事情都是显式的,因此更易于理解。

Some of Other alternatives are flummox fluxxor fluxible and redux . 其他一些替代产品都是惊讶 fluxxor 变化无常的终极版

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值