【问题标题】:Event Not Firing, Issues Communicating Between React Components事件未触发,React 组件之间的通信问题
【发布时间】:2015-11-05 18:46:03
【问题描述】:

请原谅我的幼稚,但我对 React 还很陌生,并且遇到了一些障碍。我有两个相关的组件需要在它们之间进行通信。我有一个代表选项元素(实际上是一个 div)的“下拉”组件,我需要它的值来导致不同组件的状态更改。下拉菜单应该基本上将类别设置为第二个组件的状态,该组件将根据类别显示不同的内容。我尝试了几种方法来处理这个问题,但收效甚微。目前我正在尝试使用 EventListeners 来处理这个问题,但这似乎也不起作用。

以下是我当前的代码,现在的设置方式我收到一条错误消息,提示“this.dispatchEvent 不是函数”。如果有人可以帮助我解决这个问题并继续前进,那将非常感激。或者,如果这是处理此问题的不好方法,我非常愿意接受建议。

var GridView = React.createClass({
  getInitialState: function() {
    window.addEventListener("scroll", this.handleScroll);
    return {
      data: [],
      page: 0,      //for pagination
      loadingFlag: false,
    };
    },

  getMainFeed: function() {
...

   }, //end function
   getMoreItems: function() {
...

 }, //end function
 getFilteredItems: function() {
...
}, //end function
  componentWillMount: function() {

  },
  listenForEmailChange: function() {
    window.addEventListener("selectedFilterChange", this.handleFilterChange, false);
  },
  componentWillUnmount: function() {
    window.removeEventListener("selectedFilterChange", this.handleFilterChange, false);
  },
  handleFilterChange: function(filter) {
   //Convert Data for getFilteredItems
   switch (filter.detail.filterType) {
    case 'category':
      this.setState({
        itemCategory: filter.detail.filterSelected,
      });
      break;
    case 'event':
      this.setState({
        eventType: filter.detail.filterSelected,
      });
      break;
    case 'type':
      if (0){
        this.setState({
          filterBuy: 1,
          filterInspiration: 0,
        });
      }
      if (1){
        this.setState({
          filterBuy: 0,
          filterInspiration: 1,
        });
      }
      if (2){
        this.setState({
          filterBuy: 1,
          filterInspiration: 1,
        });
      }
      break;
    case 'trending':
      this.setState({
        itemCategory: filter.detail.filterSelected,
      });
      break;
   }

//   this.getFilteredItems();
 },
  componentDidMount: function() {
    //loading("on");
    this.getMainFeed();
    MasonryInit();
    this.listenForEmailChange();
  },
  handleScroll:function(e){
    //this function will be triggered if user scrolls
    ...
  },
  componentDidUpdate: function() {
    $('#grid-container').imagesLoaded( function() {
    $('#grid-container').masonry('reloadItems');
    $('#grid-container').masonry('layout');
    });
  },
  render: function() {
      return (
        <div id="feed-container-inner">
          <GridMain data={this.state.data} />
        </div>

      );
    }
  });


      var Dropdown = React.createClass({
        sendFilter: function(item) {
          dropdownChange = new CustomEvent("selectedFilterChange", {
            detail: {
              filterType: this.props.filterSelector,
              filterSelected: item.id,
            }
          });
          window.dispatchEvent(dropdownChange);
        },
        getInitialState: function() {
          return {
            listVisible: false,
            display: ""
          };
        },

        select: function(item) {
          this.props.selected = item;
          this.sendFilter(item);
        },

        show: function() {
          this.setState({ listVisible: true });
          document.addEventListener("click", this.hide);
        },

        hide: function() {
          this.setState({ listVisible: false });
          document.removeEventListener("click", this.hide);
        },

        render: function() {
          return <div className={"dropdown-container" + (this.state.listVisible ? " show" : "")}>
            <div className={"dropdown-display" + (this.state.listVisible ? " clicked": "")} onClick={this.show}>
              <span>{this.props.selected.name}</span>
              <i className="fa fa-angle-down"></i>
            </div>
            <div className="dropdown-list">
              <div>
                {this.renderListItems()}
              </div>
            </div>
          </div>;
        },

        renderListItems: function() {
          var categories = [];
          for (var i = 0; i < this.props.list.length; i++) {
            var category = this.props.list[i];
            categories.push(<div onClick={this.select.bind(null, category)}>
              <span>{category.name}</span>
              <i className="fa fa-check"></i>
            </div>);
          }
          return categories;
        }
      });

  var GridFilter = React.createClass({
    getInitialState: function() {
      return {
        categoryList: [{
          name: "Loading Categories"
        }],
        eventList: [{
          name: "Loading Events"
        }],
        typeList: [{name: "Inspiration + Shoppable", id: 0}, {name: "Inspiration", id: 1}, {name: "Shoppable", id: 2}
        ],
        trendingList: [{
          name: "Loading Trending"
        }]

     };
    },
    getCategories: function() {

...

     }, //end function
     getEvents: function() {
...
      }, //end function
      getTrending: function() {

        ...

       }, //end function
    componentDidMount: function() {
      this.getCategories();
      this.getEvents();
      this.getTrending();
    },
    render: function() {
      return (
        <div id="filter-bar" className="stamp">
          <Dropdown filterSelector={'category'} list={this.state.categoryList} selected={this.state.categoryList[0]} />
          <Dropdown filterSelector={'event'} list={this.state.eventList} selected={this.state.eventList[0]} />
          <Dropdown filterSelector={'type'} list={this.state.typeList} selected={this.state.typeList[0]} />
          <Dropdown filterSelector={'trending'} list={this.state.trendingList} selected={this.state.trendingList[0]} />
          <p className="filter-text">Filters:</p>

        </div>
      );
    }
  });

【问题讨论】:

    标签: javascript javascript-events reactjs


    【解决方案1】:

    几个建议:

    首先,this.dispatchEvent 在 React 中不存在。您“调度事件”的方式是将function 作为道具传递,然后调用它。你的 dropdown 组件应该有一个 onSelect 属性,它是一个函数。每当用户选择某些东西时,只需调用 this.props.onSelect 并传递适当的参数即可。

    其次,你不应该像在 select 方法上那样设置 prop。如果你想在组件中存储你选择的项目的值,你应该使用 state。

    select 方法的重构版本如下所示:

      select: function(item) {
        this.setState({selected: item });
        if(this.onSelect) {
            this.onSelect(/* pass args here */);
        }
      }
    

    由于上述更改,您的其余代码可能应该被重构,这只是为了清楚起见。

    额外提示:注意 controlleduncontrolled 组件的概念。

    受控组件:几乎不存储状态的组件。它们只是在某些事情发生变化时触发事件,并且父组件有责任通过适当的props 重新渲染它。在您的情况下,如果您的组件充当controlled,它将不会保存所选项目的状态,它只会触发onSelect 事件,并且父级将重新渲染它,并将新的所选项目作为道具传递。

    不受控制的组件:将自行工作的组件,无需附加处理程序。在您的情况下,如果您的组件充当uncontrolled,即使它可能有一个onSelect 事件,它也会保持所选项目的状态,这样它就可以在没有的情况下工作附加任何事件处理程序。当发生变化时,它会重新呈现自己。

    组件通常设计为controlleduncontrolled,但也可以混合使用。

    【讨论】:

    • 感谢您提供的所有好信息。我要试试你的建议。我试图实现你重构的选择方法,但现在最初选择的项目没有出现。在我通过 '' 在下拉列表中设置最初选择的项目之前,如果它现在是一个状态,我将如何传递它?
    • 另外,关于这部分'每当用户选择某些东西时,只需调用 this.props.onSelect 并传递适当的参数。'但是,这将如何改变其他组件的状态?听起来你在谈论这个组件,但我需要下拉组件来更改 GridView 组件的状态。
    • 关于这个:'' 我建议你传递选定的索引,而不是整个对象。我没有得到这部分:如果它现在是一个状态,我将如何通过它?
    • @BillWalters,关于您的第二条评论:想象一下您的下拉菜单不受控制。现在您有了一个呈现下拉列表和网格的包装器组件。现在,在包装器中,为下拉列表分配一个事件处理程序,当它更改时,更改包装器的selectedSomething 状态。这将再次触发包装器的渲染。现在渲染下拉列表,将其作为所选项目传递,并根据它渲染网格。
    • 我更新了上面的代码,希望现在更有意义。关于您的第一条评论,我有一些问题。如何在不传递整个列表的情况下显示类别列表?我现在传递它的意思是,现在我将“选择”从一个道具更改为一个状态,最初选择的类别没有出现。
    猜你喜欢
    • 1970-01-01
    • 2015-09-18
    • 1970-01-01
    • 1970-01-01
    • 2019-09-11
    • 1970-01-01
    • 2014-08-11
    • 2018-03-23
    • 1970-01-01
    相关资源
    最近更新 更多