React-未在回调中设置props表达式的子组件数组未重新呈现

时间:2018-07-28 16:53:44

标签: javascript reactjs socket.io

我了解到该问题的措词有些含糊,因此我将进行扩展。这是我的一个个人项目,我学习了一些React基础知识并熟悉了socket.io

我有一个CollapsibleList组件和一个NestedList组件,该组件呈现CollapsibleList组件的数组。

NestedList具有一个事件处理程序,该事件处理程序在组件的componentWillMount中设置。事件是当菜单从我的服务器通过socket.io到达时。菜单到达时,新的CollapsibleList被添加到数组,并且状态被更改以触发重新渲染。这些事件由通过componentDidMount(get-menus)发出的初始socket.io事件触发。

CollapsibleList通过onclick折叠/展开,该onclick使用通过NestedList的props传递的toggleVisiblity方法,其状态确定其子CollapsibleList组件是否打开。

问题:CollapsibleList道具(来自NestedList的状态)在更改NestedList的状态时不会更改。我已经检查了调试器中的属性,并且已经停留了几天。换句话说,CollapsibleList元素出现在浏览器窗口中,但是单击它只会更改NestedList的状态,并且CollapsibleList的props不会更改,因此不会出现/消失。我认为这与在socket.io回调中创建绑定了“ this”的CollapsibleLists有关,因为CollapsibleList的“ collapsed”属性取决于this.state [restaurantId] .collapsed。来源如下,如果不清楚,我可以添加更多解释。

class CollapsibleList extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <List>
        <ListItem
          button
          onClick={() => {
            this.props.collapseEventHandler(this.props.restaurantId);
          }}
        >
          <ListItemText primary="Collapse me!" />
        </ListItem>
        <ListItem>
          <Collapse in={!this.props.collapsed} timeout="auto" unmountOnExit>
            <ListItemText primary="Hello World!" />
          </Collapse>
        </ListItem>
      </List>
    );
  }
}

class NestedList extends React.Component {
  constructor(props) {
    super(props);
    //let menuData = props.menuData.map()
    this.toggleVisiblity = this.toggleVisiblity.bind(this);
    this.arrayOfMenus = [];
  }

  componentWillMount() {
    socket.on(
      "menu-arrived",
      function(menuJson) {
        if (menuJson.response.menu.menus.items) {
          let restaurantId = menuJson.restaurant_id;
          //let menuId = menuJson.response.menu.menus.items[0].menuId;
          this.arrayOfMenus.push(
            <CollapsibleList
              collapsed={this.state[restaurantId].collapsed}
              collapseEventHandler={this.toggleVisiblity}
              restaurantId={restaurantId}
              key={restaurantId}
            />
          );
          this.setState(function(prevState, props) {
            return {
              [restaurantId]: {
                collapsed: prevState[restaurantId].collapsed,
                updated: true
              }
            };
          });
        }
      }.bind(this)
    );
  }

  componentDidMount() {
    getNearbyRestaurantRawData().then(
      function(rawData) {
        let restaurantIds = parseOutVenueIds(rawData);
        let menusOpen = {};
        for (let i = 0; i < restaurantIds.length; i++) {
          menusOpen[restaurantIds[i]] = {
            collapsed: true
          };
        }
        this.setState(menusOpen, () => {
          socket.emit("get-menus", {
            ids: restaurantIds
          });
        });
      }.bind(this)
    );
  }

  toggleVisiblity(restaurantId) {
    this.setState(function(prevState, props) {
      let newState = Object.assign({}, prevState);
      newState[restaurantId].collapsed = !prevState[restaurantId].collapsed;
      return newState;
    });
  }

  render() {
    return (
      <List>
        <React.Fragment>
          <CssBaseline>{this.arrayOfMenus}</CssBaseline>
        </React.Fragment>
      </List>
    );
  }
}

1 个答案:

答案 0 :(得分:1)

您将CollapsibleList个React元素推送到实例上的数组,这意味着当状态或道具更改时,不会创建新的React元素,也不会从render方法返回它。

您应该始终从状态和props中的render方法中获取UI。

示例

class NestedList extends React.Component {
  state = { restaurantIds: [] };

  componentWillMount() {
    socket.on("menu-arrived", menuJson => {
      if (menuJson.response.menu.menus.items) {
        let restaurantId = menuJson.restaurant_id;
        this.setState(prevState => {
          return {
            restaurantIds: [...prevState.restaurantIds, restaurantId],
            [restaurantId]: {
              collapsed: prevState[restaurantId].collapsed,
              updated: true
            }
          };
        });
      }
    });
  }

  // ...

  render() {
    return (
      <List>
        <React.Fragment>
          <CssBaseline>
            {this.state.restaurantIds.map(restaurantId => (
              <CollapsibleList
                collapsed={this.state[restaurantId].collapsed}
                collapseEventHandler={this.toggleVisiblity}
                restaurantId={restaurantId}
                key={restaurantId}
              />
            ))}
          </CssBaseline>
        </React.Fragment>
      </List>
    );
  }
}