8

I'm trying to cache the rendered markup of the App component. I know that this is somehow "against the rules" but I'm in a server-less environment (chrome-extension). Upon page load i want to inject the cached App markup into the DOM. The expected result is similar to the experience of having the react-component rendererd on a server. Very much as described here: http://www.tabforacause.org/blog/2015/01/29/using-reactjs-and-application-cache-fast-synced-app/.

To illustrate my usecase, I have updated the Thinking in react example:

  • App
    • FilterableProductTable
      • SearchBar
      • ProductTable (containing from reflux store in state)
        • ProductCategoryRow
        • ProductRow

As is expected, neither componentDidUpdate nor componentWillUpdate are called in App.

Is it possible to detect updated child components in the App component in a sane way? Preferably without modifying the child component classes?

I would like to avoid moving props/state to App.

4

6 回答 6

6

我想出了一个解决方案,它可以作为解决方案的一部分(无需修改子组件,或了解整个应用程序状态,例如:Flux 模式):

App可以包装在一个组件中,该组件使用MutationObserver来跟踪 DOM 中的实际更改。

于 2016-05-23T11:10:37.843 回答
2

您可以在 App 中定义一个回调,该回调通过 props 通过其子层次结构向下传递,如果调用子组件的 componentDidUpdate 方法则触发该回调。但是,如果您有很多孩子的层次结构很深,这可能会变得混乱。

于 2015-03-13T20:21:33.443 回答
0

我有一种情况,我想this.setProps(…)在单元测试中进行(当组件在没有父级的情况下呈现时)。但是,如果在有父母的情况下完成,则会导致错误。

我的解决方法是简单地<MyComponent renderingWithoutParentForTest={true} />在单元测试中设置一个道具,并将该道具用于条件。

不过,我承认这很丑陋。在这种特殊情况下,这似乎是有道理的。

于 2015-09-10T11:58:00.563 回答
0

如果你有更大的应用程序,事件系统是比传递道具更好的解决方案。

按照助焊剂的建议进行思考。组件 -> 操作 -> 调度程序 -> 存储

在商店中,您将拥有自己的状态。您将注册要存储的组件的回调。您从任何组件和任何其他组件触发操作,即监听商店的更改正在获取数据。无论您如何更改层次结构,您始终可以在需要的地方获取数据。

调度程序.js:

var Promise = require('es6-promise').Promise;
var assign = require('object-assign');

var _callbacks = [];
var _promises = [];

var Dispatcher = function () {
};

Dispatcher.prototype = assign({}, Dispatcher.prototype, {

    /**
     * Register a Store's callback so that it may be invoked by an action.
     * @param {function} callback The callback to be registered.
     * @return {number} The index of the callback within the _callbacks array.
     */

    register: function (callback) {
        _callbacks.push(callback);
        return _callbacks.length - 1;
    },

    /**
     * dispatch
     * @param  {object} payload The data from the action.
     */

    dispatch: function (payload) {
        var resolves = [];
        var rejects = [];
        _promises = _callbacks.map(function (_, i) {
            return new Promise(function (resolve, reject) {
                resolves[i] = resolve;
                rejects[i] = reject;
            });
        });

        _callbacks.forEach(function (callback, i) {
            Promise.resolve(callback(payload)).then(function () {
                resolves[i](payload);
            }, function () {
                rejects[i](new Error('#2gf243 Dispatcher callback unsuccessful'));
            });
        });
        _promises = [];
    }
});

module.exports = Dispatcher;

一些商店样品:

const AppDispatcher = require('./../dispatchers/AppDispatcher.js');
const EventEmitter = require('events').EventEmitter;
const AgentsConstants = require('./../constants/AgentsConstants.js');
const assign = require('object-assign');

const EVENT_SHOW_ADD_AGENT_FORM = 'EVENT_SHOW_ADD_AGENT_FORM';
const EVENT_SHOW_EDIT_AGENT_FORM = 'EVENT_SHOW_EDIT_AGENT_FORM';

const AgentsStore = assign({}, EventEmitter.prototype, {

    emitShowAgentsAddForm: function (data) {
        this.emit(EVENT_SHOW_ADD_AGENT_FORM, data);
    },
    addShowAgentsAddListener: function (cb) {
        this.on(EVENT_SHOW_ADD_AGENT_FORM, cb);
    },
    removeShowAgentsAddListener: function (cb) {
        this.removeListener(EVENT_SHOW_ADD_AGENT_FORM, cb);
    }

});

AppDispatcher.register(function (action) {

    switch (action.actionType) {
        case AgentsConstants.AGENTS_SHOW_FORM_EDIT:
            AgentsStore.emitShowAgentsEditForm(action.data);
            break;
        case AgentsConstants.AGENTS_SHOW_FORM_ADD:
            AgentsStore.emitShowAgentsAddForm(action.data);
            break;
    }
});


module.exports = AgentsStore;

动作文件:

var AppDispatcher = require('./../dispatchers/AppDispatcher.js');
var AgentsConstants = require('./../constants/AgentsConstants.js');

var AgentsActions = {

    show_add_agent_form: function (data) {
        AppDispatcher.dispatch({
            actionType: AgentsConstants.AGENTS_SHOW_FORM_ADD,
            data: data
        });
    },
    show_edit_agent_form: function (data) {
        AppDispatcher.dispatch({
            actionType: AgentsConstants.AGENTS_SHOW_FORM_EDIT,
            data: data
        });
    },
}

module.exports = AgentsActions;

在某些组件中,您就像:

...
    componentDidMount: function () {
        AgentsStore.addShowAgentsAddListener(this.handleChange);
    },
    componentWillUnmount: function () {
        AgentsStore.removeShowAgentsAddListener(this.handleChange);
    },
...

这段代码有点旧,但运行良好,您绝对可以了解这些代码是如何工作的

于 2016-05-22T19:20:02.357 回答
0

如果您只想知道孩子编号何时更改,或者您可以访问每个孩子的 React.Children.map/forEach,则可以使用 React.Children.count。

查看这个示例(我在 useEffect 挂钩中使用它,但您可以在 componentDidMount 或 DidUpdate 中使用它)

const BigBrother = props => {
   const { children } = props;
   const childrenIds = React.Children.map(children, child => {
      return child ? child.props.myId : null;
   }).filter(v => v !== null);
   useEffect(() => {
      // do something here
   }, [childrenIds.join("__")]);

  return (
    <div>
      <h2>I'm the big brother</h2>
      <div>{children}</div>
    </div>
}

然后你可以像这样使用它(使用动态列表!)

<BigBrother>
  <LilBrother myId="libindi" />
  <LilBrother myId="lisoko" />
  <LilBrother myId="likunza" />
</BigBrother>
于 2019-11-13T09:01:12.190 回答
0

React 文档提出了两种处理孩子与父母沟通的方法。第一个已经提到,它是将一个函数作为 props 从父组件通过层次结构向下传递,然后在子组件中调用它们。

孩子与父母的沟通:https ://facebook.github.io/react/tips/communicate-between-components.html

二是使用全局事件系统。您可以构建自己的事件系统,该系统可以相当容易地用于这些目的。它可能看起来像这样:

var GlobalEventSystem = {

  events: {},

  subscribe: function(action, fn) {
    events[action] = fn;
  },

  trigger: function(action, args) {
    events[action].call(null, args);
  }
};

var ParentComponent = React.createClass({

  componentDidMount: function() {
    GlobalEventSystem.subscribe("childAction", functionToBeCalledWhenChildTriggers);
  },

  functionToBeCalledWhenChildTriggers: function() {
    // Do things
  }
)};

var DeeplyNestedChildComponent = React.createClass({

   actionThatHappensThatShouldTrigger: function() {
     GlobalEventSystem.trigger("childAction");
   }
});

这与 Flux 模式的功能有些相似。使用 Flux 架构可能有助于解决您的问题,因为视图组件订阅事件的想法是 Flux 的重要组成部分。因此,您将让父组件订阅 Store(s) 中的某个事件,这些事件将由子组件触发。

于 2016-02-04T21:16:26.090 回答