javascript - 数组属性更新后子组件未更新

标签 javascript arrays reactjs javascript-objects

更新数组(对象内部)时,通过向其添加对象,不会重新渲染子组件。然而,父组件是。

我尝试更新对象的非数组属性,同时也更新对象的数组属性,然后子组件将更新。例如:

不起作用:

obj.arr.push(user);

作品:

obj.arr.push(user);
obj.test = "wow";

我的问题存在于 users 属性,从 Lobby 组件传递到 Users 组件。当用户加入时,触发套接字事件lobby_player_joined,修改users数组。

大厅组件(父级):

...

const StyledTabs = styled(Tabs)`${TabsStyle};`;

class Lobby extends Component {
  constructor(props) {
    super(props);
    this.state = {
      tab: 0,
    };
    this.props.setTitle('Lobby');
  }

  static get propTypes() {
    return {
      history: PropTypes.shape({ push: PropTypes.func.isRequired }).isRequired,
      location: PropTypes.shape({ state: PropTypes.object }).isRequired,
      setTitle: PropTypes.func.isRequired,
      initializeSocket: PropTypes.func.isRequired,
      onceSocketMessage: PropTypes.func.isRequired,
      onSocketMessage: PropTypes.func.isRequired,
      sendSocketMessage: PropTypes.func.isRequired,
    };
  }

  async componentDidMount() {
    await this.props.initializeSocket((error) => {
      console.error(error);
    });

    await this.props.onSocketMessage('exception', (error) => {
      console.log(error);
    });

    await this.props.onceSocketMessage('lobby_joined', (lobby) => {
      this.setState({ lobby });
    });

    await this.props.sendSocketMessage('lobby_join', {
      id: this.props.location.state.id,
      password: this.props.location.state.password,
    });

    await this.props.onSocketMessage('lobby_player_joined', (user) => {
      const { lobby } = this.state;
      lobby.users.push(user);
      return this.setState({ lobby });
    });

    await this.props.onSocketMessage('lobby_player_left', (user) => {
      const { lobby } = this.state;
      const userIndex = lobby.users.findIndex(u => u.id === user.id);
      if (userIndex !== -1) {
        lobby.users.splice(userIndex, 1);
        this.setState({ lobby });
      }
    });

    await this.props.onSocketMessage('lobby_new_host', (host) => {
      const { lobby } = this.state;
      lobby.host = host;
      return this.setState({ lobby });
    });
  }

  handleTab = (event, value) => {
    console.log(this.state.lobby);
    this.setState({ tab: value });
  };

  handleSwipe = (value) => {
    this.setState({ tab: value });
  };

  render() {
    if (!this.state.lobby) {
      return (<div> Loading... </div>);
    }

    return (
      <Container>
        <AppBar position="static">
          <StyledTabs
            classes={{
              indicator: 'indicator-color',
            }}
            value={this.state.tab}
            onChange={this.handleTab}
            fullWidth
            centered
          >
            <Tab label="Users" />
            <Tab label="Info" />
          </StyledTabs>
        </AppBar>
        <SwipeableViews
          style={{ height: 'calc(100% - 48px)' }}
          containerStyle={{ height: '100%' }}
          index={this.state.tab}
          onChangeIndex={this.handleSwipe}
        >
          <TabContainer>
            <Users
              {...this.state.lobby}
            />
          </TabContainer>
          <TabContainer>
            <Info
              {...this.state.lobby}
            />
          </TabContainer>
        </SwipeableViews>
      </Container>
    );
  }
}

...

用户组件(子):

...

class Users extends Component {
  state = {
    isReady: false,
    usersReady: [],
  };

  async componentDidMount() {
    await this.props.onSocketMessage('lobby_user_ready', (data) => {
      this.setState(prevState => ({
        usersReady: [...prevState.usersReady, data.socketId],
      }));
    });

    await this.props.onSocketMessage('lobby_user_unready', (data) => {
      this.setState(prevState => ({
        usersReady: prevState.usersReady.filter(id => id !== data.socketId),
      }));
    });
  }

  componentWillUnmount() {
    this.props.offSocketMessage('lobby_user_ready');
    this.props.offSocketMessage('lobby_user_unready');
  }

  static get propTypes() {
    return {
      id: PropTypes.number.isRequired,
      users: PropTypes.arrayOf(PropTypes.object).isRequired,
      userCount: PropTypes.number.isRequired,
      host: PropTypes.shape({
        username: PropTypes.string.isRequired,
      }).isRequired,
      sendSocketMessage: PropTypes.func.isRequired,
      onSocketMessage: PropTypes.func.isRequired,
      offSocketMessage: PropTypes.func.isRequired,
    };
  }

  readyChange = () => {
    this.setState(prevState => ({ isReady: !prevState.isReady }), () => {
      if (this.state.isReady) {
        return this.props.sendSocketMessage('lobby_user_ready', { id: this.props.id });
      }
      return this.props.sendSocketMessage('lobby_user_unready', { id: this.props.id });
    });
  };

  renderStar = (user) => {
    const { host } = this.props;
    if (host.username === user.username) {
      return (<Icon>star</Icon>);
    }
    return null;
  }

  render() {
    return (
      <UserContainer>
        { this.props.users.length }
        <CardsContainer>
          {this.props.users.map(user => (
            <UserBlock
              className={this.state.usersReady.includes(user.socketId) ? 'flipped' : ''}
              key={user.socketId}
            >
              <BlockContent className="face front">
                { this.renderStar(user) }
                <div>{user.username}</div>
                <Icon className="icon">
                  close
                </Icon>
              </BlockContent>
              <BlockContent className="face back">
                <Icon>
                  star
                </Icon>
                <div>{user.username}</div>
                <Icon className="icon">
                  check
                </Icon>
              </BlockContent>
            </UserBlock>
          ))}
        </CardsContainer>
        <InfoContainer>
          <p>Players</p>
          <p>
            {this.props.users.length}
            {' / '}
            {this.props.userCount}
          </p>
          <p>Ready</p>
          <p>
            {this.state.usersReady.length}
            {' / '}
            {this.props.userCount}
          </p>
        </InfoContainer>
        <StyledButton
          variant={this.state.isReady ? 'outlined' : 'contained'}
          color="primary"
          onClick={this.readyChange}
        >
          { this.state.isReady ? 'Unready' : 'ready'}
        </StyledButton>
      </UserContainer>
    );
  }
}

...

谁能帮我在修改数组属性时更新/重新渲染 Users 组件?

最佳答案

不要改变状态。使用这样的东西

await this.props.onSocketMessage('lobby_player_joined', (user) => {
  const { lobby } = this.state;
  return this.setState({ lobby : {...lobby, users: lobby.users.concat(user)} });
});

编辑:修复缺失的括号

关于javascript - 数组属性更新后子组件未更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54692289/

相关文章:

c++ - 是否可以在数组中存储 1000 万个数字?

javascript - CORS 策略和 customerchat net::ERR_FAILED 上的 FB 聊天插件错误

javascript - 如何在没有文档的情况下学习 CouchDB API?

javascript - 为什么 jQuery 中的 click() 方法与 DOM click method() 的行为不同?

javascript - 如何禁用 PDF.js 渲染的 PDF 中的超链接

python - 将 numpy 数组设置为 None 是否释放内存?

javascript - 反向数组循环 - 当超过索引 0 时返回到最后一个索引

javascript - 组件渲染两次

reactjs - react : Difference between a Stateful Class Component and a Function Component using Hooks?

javascript - 我应该为大型企业应用程序使用 Material-UI 吗?