Javascript 如何在react.js中检测父组件中的子渲染

Javascript 如何在react.js中检测父组件中的子渲染,javascript,reactjs,refluxjs,Javascript,Reactjs,Refluxjs,我正在尝试缓存App组件的渲染标记。我知道这在某种程度上是“违反规则”的,但我处于一个没有服务器的环境(chrome扩展)。在页面加载时,我想将缓存的App标记注入DOM。预期结果类似于在服务器上呈现react组件的体验。非常像这里描述的: 为了说明我的用例,我更新了: 应用程序 可过滤产品表 搜索栏 ProductTable(包含来自回流的存储状态) 产品类别 ProductRow 正如预期的那样,在App中既不调用componentdiddupdate也不调用componen

我正在尝试缓存
App
组件的渲染标记。我知道这在某种程度上是“违反规则”的,但我处于一个没有服务器的环境(chrome扩展)。在页面加载时,我想将缓存的
App
标记注入DOM。预期结果类似于在服务器上呈现react组件的体验。非常像这里描述的:

为了说明我的用例,我更新了:

  • 应用程序
    • 可过滤产品表
      • 搜索栏
      • ProductTable(包含来自
        回流的
        存储状态)
        
        • 产品类别
        • ProductRow
正如预期的那样,在
App
中既不调用
componentdiddupdate
也不调用
componentWillUpdate

是否可以在
App
组件中以正常的方式检测更新的子组件?最好不修改子组件类


我希望避免将props/state移动到
App

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

我曾经遇到过这样的情况:我想在单元测试中执行此操作。
setProps(…)
(当组件在没有父级的情况下呈现时)。但如果在有父对象的情况下执行,则会导致错误

我的解决方法只是在单元测试中设置一个类似于
的道具,并使用该道具作为条件


不过,我知道这很难看。在这种特殊情况下,这似乎是有意义的。

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

孩子与家长的沟通:

第二个是使用全局事件系统。您可以构建自己的事件系统,该系统可以非常轻松地用于这些目的。它可能看起来像这样:

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的建议思考。组件->操作->调度程序->存储

在商店里你会有你的状态。您将注册要存储的组件回调。您可以从任何组件和任何其他组件启动操作,这些组件正在侦听存储区的更改并获取数据。无论您如何更改层次结构,您总是可以在需要的地方获取数据

dispatcher.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);
    },
...

这段代码有点旧,但它工作得很好,您肯定可以了解东西是如何工作的

我提出了一个解决方案,它可以作为一个临时解决方案工作(不需要修改子组件,也不需要了解整个应用程序状态,例如:Flux模式):


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

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

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

const BigBrother=props=>{
const{children}=props;
const childrenIds=React.Children.map(Children,child=>{
返回child?child.props.myId:null;
}).filter(v=>v!==null);
useffect(()=>{
//在这里做点什么
},[childrenIds.join(“”)];
返回(
我是老大哥
{儿童}
}
然后你可以像这样使用它(使用动态列表tho!)



您试图解决的问题是
应用程序
组件需要了解子组件更改的地方?@wiredparie我已经更新了问题,以包括更大的图片。您需要修改所有可能的子组件,这是我希望避免的。
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>