SetState后React不更新渲染 [英] React Not Updating Render After SetState

查看:54
本文介绍了SetState后React不更新渲染的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经玩了一段时间了.我正在从Firebase抓取数据,并将该数据中的对象列表填充到此UserProfile页面上.我只希望它能正常工作,以便当我进入个人资料页面时,在那里会列出他们的项目清单.

I've played around with the code for a while now. I'm grabbing data from Firebase and populating a list of objects from that data onto this UserProfile page. I just want it to work so that when I go on the profile page, a list of their items is listed there.

问题在于,使用此版本的代码,我必须单击配置文件链接两次才能显示项目,但显示名称显示正常.我知道setState是异步的.我试过在setState的回调中设置状态.我已经尝试检查快照是否事先存在.我已经尝试了componentDidMount和componentDidUpdate.这些东西都没有帮助,我只是以this.state.items.map结尾,或者newState是一些空的,怪异的东西.我不知道我现在要去哪里.

The problem is that with this version of the code I have to click on the profile link twice for the items to show up, but the display name shows up fine. I know setState is asynchronous. I've tried setting the state in a callback in setState. I've tried checking if the snapshot exists beforehand. I've tried componentDidMount and componentDidUpdate. None of these things have helped, I just end up with this.state.items.map can't be called or newState is some empty, weird thing. I don't know where I'm going wrong now.

当我进行控制台调试时,当到目前为止从Firebase尚未获取任何东西且以后再也不会调用setState时,似乎正在调用setState.

When I console debug this, it looks like setState is being called when nothing has been fetched from Firebase so far and never gets called later.

也许我设置newState的方式有问题,因为当我尝试首次设置它时,当我控制台日志时newState为空.我只是不知道何时在适当的时间为第一个渲染设置此项.

Maybe something is wrong with how I set newState because when I console log newState is empty when I'm trying to first set it. I just don't know when to set this at the appropriate time for the first render.

class UserProfile extends Component {
  constructor(props){
    super(props)
    this.state = {
      redirect: false,
      userId: this.props.match.params.id,
      displayName: "",
      items: []
    }
    this.userRef = firebaseDB.database().ref(`/Users/${this.state.userId}`)
    this.usersItemsRef = firebaseDB.database().ref(`/Users/${this.state.userId}/items`)
  }

  componentWillMount() {
    this.userRef.on('value', snapshot => {
      this.setState({
        displayName: snapshot.val().displayName
      });
    });
    this.usersItemsRef.on('value', snapshot => {
      let usersItems = snapshot.val();
      let newState = [];
      for (let userItem in usersItems) {
        var itemRef = firebaseDB.database().ref(`/items/${userItem}`)
        itemRef.once('value', snapshot => {
          var item = snapshot.val()
          newState.push({
            id: itemRef.key,
            title: item.title
          });
        })
      }
      this.setState({
        items: newState
      })
    });
  }

  componentWillUnmount() {
    this.userRef.off();
    this.usersItemsRef.off();
  }

  render() {
    return (
        <div style={{marginTop: "100px"}}>
          <h1>{this.state.displayName}</h1>
          <Link className="pt-button" aria-label="Log Out" to={"/submit_item/"+this.state.userId} >Submit an item</Link>
          <section className='display-itemss'>
              <div className="wrapper">
                <ul>
                  {this.state.items.map((item) => {
                    return (
                      <li key={item.id}>
                        <h3>{item.title}</h3>
                      </li>
                    )
                  })}
                </ul>
              </div>
          </section>
        </div>
      );
  }
}

推荐答案

从Firebase异步加载数据,因为它可能需要不确定的时间.当数据可用时,Firebase会调用您传入的回调函数.但是到那时,您对 setState()的调用早已结束.

Data is loaded from Firebase asynchronously, since it may take an undetermined amount of time. When the data is available, Firebase calls the callback function you passed in. But by that time your call to setState() has long finished.

最简单的方法是在代码中添加一些日志语句:

The easiest way to see this is to add a few log statements to your code:

componentWillMount() {
  this.userRef.on('value', snapshot => {
    this.setState({
      displayName: snapshot.val().displayName
    });
  });
  this.usersItemsRef.on('value', snapshot => {
    let usersItems = snapshot.val();
    let newState = [];
    console.log("Before attaching once listeners");
    for (let userItem in usersItems) {
      var itemRef = firebaseDB.database().ref(`/items/${userItem}`)
      itemRef.once('value', snapshot => {
        console.log("In once listener callback");
        var item = snapshot.val()
        newState.push({
          id: itemRef.key,
          title: item.title
        });
      })
    }
    console.log("After attaching once listeners");
    this.setState({
      items: newState
    })
  });
}

此日志记录的输出将是:

The output from this logging will be:

在附加一次听众之前

Before attaching once listeners

添加一次听众之后

一次侦听器回调

一次侦听器回调

...

这可能不是您期望的顺序.但这很好地说明了为什么您的 setState()不更新UI:尚未加载数据.

This is probably not the order you expected. But it explains perfectly why your setState() doesn't update the UI: the data hasn't been loaded yet.

解决方案是在加载数据后调用 setState().您可以通过将其移动"到回调中来完成此操作:

The solution is to call setState() when the data has been loaded. You do this by moving it **into* the callback:

componentWillMount() {
  this.userRef.on('value', snapshot => {
    this.setState({
      displayName: snapshot.val().displayName
    });
  });
  this.usersItemsRef.on('value', snapshot => {
    let usersItems = snapshot.val();
    let newState = [];
    for (let userItem in usersItems) {
      var itemRef = firebaseDB.database().ref(`/items/${userItem}`)
      itemRef.once('value', snapshot => {
        var item = snapshot.val()
        newState.push({
          id: itemRef.key,
          title: item.title
        });
        this.setState({
          items: newState
        })
      })
    }
  });
}

这将为加载的每个项目调用 setState().通常,React可以很好地处理此类增量更新.但是,以防万一导致闪烁,您也可以使用 Promise.all():

This will call setState() for every item that is loaded. Usually React is pretty good with handling such incremental updates. But just in case it causes flicker, you can also wait for all items to be loaded by using Promise.all():

componentWillMount() {
  this.userRef.on('value', snapshot => {
    this.setState({
      displayName: snapshot.val().displayName
    });
  });
  this.usersItemsRef.on('value', snapshot => {
    let usersItems = snapshot.val();
    let newState = [];
    let promises = [];
    for (let userItem in usersItems) {
      var itemRef = firebaseDB.database().ref(`/items/${userItem}`)
      promises.push(itemRef.once('value'));
    }
    Promise.all(promises).then((snapshots) => {
      snapshots.forEach((snapshot) => {
        var item = snapshot.val()
        newState.push({
          id: itemRef.key,
          title: item.title
        });
      });
      this.setState({
        items: newState
      });
    });
  });
}

这篇关于SetState后React不更新渲染的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆