ReactJs + Reflux // триггер выполняется на нулевом компоненте?

Я пытаюсь использовать Reflux в своем приложении ReactJs.

У меня возникла проблема, когда компонент, прослушивающий хранилище данных, выполняет обратный вызов для пустого экземпляра компонента, а не для фактического компонента, который был создан.

При вызове действия toggleUserBar вызывается хранилище данных и выполняется триггер. В компоненте Боковая панель вызывается обратный вызов, но состояние компонента пустое:

Uncaught TypeError: не удается прочитать свойство «sd» неопределенного

Любая идея, что я делаю неправильно?

actions.tsx

var Reflux = require('reflux');
var RQ = {};

RQ.actions = Reflux.createActions([
    "toggleUserBar"
]);

window.RQ = RQ;

store.tsx

RQ.store = Reflux.createStore({
  listenables: [RQ.actions],
  init: function() {},
  onToggleUserBar: function() {
      this.trigger();
  }
});

сайдбар.tsx

import * as React from "react";
import * as ReactDOM  from "react-dom";

const LeftNav = require('material-ui/lib/left-nav');
const Menu = require('material-ui/lib/menu/menu')

interface ISidebarProps extends React.Props<any> {

}

interface ISidebarState extends React.Props<any> {
    shown: Boolean;
    sd: any;
}

export class Sidebar extends React.Component<ISidebarProps, ISidebarState> {

  unsubscribe: any;

  constructor(props: ISidebarProps) {
      super(props);
      this.state = { shown: true, sd: null };
  };

  componentDidMount() {
    this.unsubscribe = RQ.store.listen(function() {
      this.state.sd.setState({ open: true });
    });
  };

  componentWillUnmount() {
      this.unsubscribe();
  };

  render() {

    let menuItems = [
      { route: 'get-started', text: 'Get Started' },
      { route: 'customization', text: 'Customization' },
      { route: 'components', text: 'Components' }
    ];

    return (
      <LeftNav ref={(c) => this.state.sd = c} docked={false} openRight={true} menuItems={menuItems} />
    );

  };

}

Компонент боковой панели отображается в другом компоненте:

<ComSidebar ref={(c) => sdref = c} />

Если я вручную вызываю sdref.state.sd.open = true, все работает нормально.


person NGPixel    schedule 19.12.2015    source источник


Ответы (2)


Ключевое слово this в JavaScript часто ведет себя неожиданным образом. Вы нашли главное.

Когда вы создаете внутреннюю функцию с помощью ключевого слова function, она не получает ту же переменную this, что и объемлющая функция. Итак, это означает, что в вашем коде:

componentDidMount() {
  // "this" out here is your component instance
  this.unsubscribe = RQ.store.listen(function() {
    // "this" in here is NOT the same - undefined, as you're seeing
    this.setState({ open: true });
  });
};

Есть несколько способов исправить это. Поскольку вы используете ES6, проще всего, вероятно, использовать стрелочную функцию - стрелочные функции явно сохраняют ссылку this своей закрывающей функции. Это будет выглядеть так:

componentDidMount() {
  this.unsubscribe = RQ.store.listen(() => {
    // now it's the same "this"
    this.setState({ open: true });
  });
};

Если бы вы не использовали ES6, вы могли бы получить такое же поведение, вызвав bind для этой функции (что позволяет указать переменную this, а также любые ее параметры):

componentDidMount() {
  this.unsubscribe = RQ.store.listen(function() {
    this.setState({ open: true });
  }.bind(this)); 
  // by calling bind, we are explicitly setting the "this" variable here
  // passing it from outer scope to inner scope
};
person Paul Phillips    schedule 19.12.2015

this.state.sd.setState({open: true}); должно быть this.setState({open: true}).

export class Sidebar extends React.Component<ISidebarProps, ISidebarState> {

  unsubscribe: any;

  constructor(props: ISidebarProps) {
      super(props);
      this.state = { shown: true, sd: null };
      this.storeDidChange = this.storeDidChange.bind(this);
  };

  componentDidMount() {
    this.unsubscribe = RQ.store.listen(this.storeDidChange);
  };

  componentWillUnmount() {
      this.unsubscribe();
  };

  storeDidChange() {
    this.refs.leftnav.open = true;
  }

  render() {

    let menuItems = [
      { route: 'get-started', text: 'Get Started' },
      { route: 'customization', text: 'Customization' },
      { route: 'components', text: 'Components' }
    ];

    return (
      <LeftNav ref={leftnav} docked={false} openRight={true} menuItems={menuItems} />
    );

  };

}

person J. Mark Stevens    schedule 19.12.2015
comment
К сожалению, я понимаю, что this.setState не является функцией. Похоже, что это по какой-то причине недействительно... - person NGPixel; 19.12.2015