简体   繁体   English

React.js:对外部对象的变化做出反应

[英]React.js: react on changes in external object

After reading official react.js documentation I understand how it should work in a good way, like阅读官方react.js文档后,我了解它应该如何以良好的方式工作,例如

  1. I have list of items in initial component state我有处于初始组件状态的项目列表
  2. adding new item through setState will update state and trigger update of UI通过setState添加新项目将更新状态并触发 UI 更新

What should I do if I use external object as model like some global array which should be available for some not react.js parts of code OR could be modified with web sockets somewhere in future?如果我使用外部对象作为模型,比如一些全局数组,它应该可用于某些非react.js代码部分,或者将来可以在某个地方使用 Web 套接字进行修改,我该怎么办? Is calling ReactDOM.render after each action a good way?在每个动作之后调用ReactDOM.render是一种好方法吗? AFAIK it should work ok from performance point of view. AFAIK 从性能的角度来看,它应该可以正常工作。

You still use setState :你仍然使用setState

let React = require('React');
let externalThing = require('tools/vendor/whoever/external-lib');

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = this.getInitialState();
  }
  getInitialState() {
    // This assumes your external thing is written by someone who was
    // smart enough to not allow direct manipulation (because JS has
    // no way to monitor primitives for changes), and made sure
    // to offer API functions that allow for event handling etc.
    externalThing.registerChangeListener(() => this.updateBasedOnChanges(externalThing));
    return { data: externalThing.data }        
  }
  updateBasedOnChanges(externalThing) {
    // note that setState does NOT automatically trigger render(),
    // because React is smarter than that. It will only trigger
    // render() if it sees that this new 'data' is different
    // (either by being a different thing entirely, or having
    // different content)
    this.setState({
      data: externalThing.data
    });
  }
  render() {
    // ...
  }
}

If the external thing you're using is terribly written and you have to manipulate its data directly, your first step is to write an API for it so you don't directly manipulate that data.如果你使用的外部东西写得很糟糕,你必须直接操作它的数据,你的第一步是为它编写一个 API,这样你就不会直接操作这些数据。

let externalData = require('externaldata') // example: this is a shared array
let ExternalDataAPI = new ExternalDataAPI(externalData);
...

And then you make sure that API has all the update and event hooks:然后确保 API 具有所有更新和事件挂钩:

class ExternalDataAPI {
  constructor(data) {
    this.data = data;
    this.listeners = [];
  }
  addListener(fn) {
    this.listeners.push(fn);
  }
  update(...) {
    // do something with data
    this.listeners.forEach(fn => fn());
  }
  ...
}

Alternatively, there are frameworks that already do this for you (flux, etc) but they also somewhat dictate how many more things "should be done" so that might be overkill for your need.或者,有些框架已经为您执行了此操作(flux 等),但它们也在某种程度上决定了“应该完成”多少事情,因此这可能对您的需求来说太过分了。

Since your question is about organizing your code in a manageable way, I would first of all suggest pairing ReactJS with a Flux-type framework, like Redux or Relay.由于您的问题是以可管理的方式组织代码,因此我首先建议将 ReactJS 与 Flux 类型的框架(如 Redux 或 Relay)配对。

If you want to skip that for now, then you can organize your project using some react components at the top of the hierarchy for storing and retrieving data.如果您现在想跳过它,那么您可以使用层次结构顶部的一些反应组件来组织您的项目,以存储和检索数据。 For example, in such a component, in its componentWillMount method, you can start a setTimeout that periodically checks your global array and calls setState when appropriate.例如,在这样的组件中,在它的componentWillMount方法中,您可以启动一个setTimeout ,它会定期检查您的全局数组并在适当的时候调用setState The render method should then contain child components that receive this state as props.然后 render 方法应该包含接收这个状态作为 props 的子组件。

Below is an example.下面是一个例子。 Obviously, the timers can be replaced by whichever method you use to subscribe to your data changes.显然,计时器可以被您用来订阅数据更改的任何方法替换。

 // your global object var globalState = {name: "Sherlock Holmes"} function onData(callback) { setInterval(function(){ callback(globalState) }, 1500) } var Child = React.createClass({ render: function() { return <h1>Hello, {this.props.name}</h1>; } }); var Root = React.createClass({ getInitialState: function() { return {} }, componentWillMount: function() { var that = this; this.props.onData(function(data){ that.setState({external: data}) }) }, render: function() { if (this.state.external) return <Child name={this.state.external.name}/> else return <div>loading...</div>; } }); ReactDOM .render(<Root onData={onData} />, document.getElementById('container'))
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script> <div id="container"></div>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM