【问题标题】:Vue waiting for data from props before usingVue 在使用前等待来自 props 的数据
【发布时间】:2020-05-08 12:14:20
【问题描述】:

我一直遇到一个问题,我正在等待来自道具的数据,以便在我创建的钩子中做某事。它不断抛出诸如“this.value is not defined”之类的错误(因为它还没有从prop加载)

我已使用v-if 修复了其中一些问题,并且仅在需要时使用。但是在某些情况下,我想在我的创建中使用该道具信息,当发生这种情况时,我最终会遇到错误并且不知道如何最好地告诉创建的那部分在运行之前“等待”数据。

有什么建议吗?

--

更新以添加一些示例代码。

在 App.js 页面上,我正在调用“userInfo”并将其设置为这样的数据。我这样做有两个原因。首先检查用户是否登录,其次花时间保存用户信息,因为它将在以后在整个应用程序中使用。 (请注意,user 和 userInfo 不同,包含不同的信息。User 是电子邮件/密码,而 userInfo 是用户名、简历等)

data() {
  return {
    user: null,
    userInfo: null,
  }
},
created(){
//let user = firebase.auth().currentUser
  firebase.auth().onAuthStateChanged((user) => {
    if(user){
      this.user = user
      let ref = db.collection('users')
      .where('user_id', '==', this.user.uid)
      ref.get().then(snapshot => {
      snapshot.forEach(doc => {
        this.userInfo = doc.data()
      })
    })

    } else {
      this.user = null
    }
  })
},

现在,我在使用 v-bind 中传递这些信息。然后在我需要一些信息的地方使用道具调用它。

当我想做一些事情时,问题就来了 -

if(this.userInfo) {
  value = true  
}

调用this.userInfo会抛出错误“this.userInfo is null”(我在页面的data()中默认设置为null)。

【问题讨论】:

  • 您是否尝试过使用mounted 中的变量而不是created
  • 您可能会分享您的代码示例。但是,您正在描述一种状态,其中传入的道具甚至还不存在,就像一个承诺响应。可能是这个原因吗?
  • @Aer0 可能,尽管我将道具从 App.vue 传递到页面。它确实加载并在方法中可用,但在文档加载时还没有准备好(或者即使在安装时,似乎也是如此)
  • @Jesper 我刚刚尝试了一下,但它似乎仍然为时过早?
  • @KJParker我假设您与我们共享一些代码。可能更容易追查问题。但是,我仍然认为这是一个承诺/异步问题。承诺在方法中使用时得到解决,但不是在开始时(创建的钩子)。

标签: firebase vue.js vuejs2


【解决方案1】:

所以我认为这里接受的答案并不能完全解决 OP 的问题,因为仍然存在一些边缘情况,如果 API 返回,您的 prop 字面上会变为空或 null,或者您可能正在处理一些您不知道的项目'无法控制父组件。所以在这些情况下,你可以通过检查 prop 在 created 钩子中是否已经可用来在内部处理这个问题,如果没有,你应该动态观察这个 prop,直到它可用。

// your component
export default {
  props: ['userInfo'],

  created() {
    // check if the prop userInfo available or not
    if (this.userInfo) {
      // do your thing
    } else {
      // start watching this prop
      const unwatch = this.$watch('userInfo', () => {
        if (!this.userInfo) return

        // do your thing when userInfo is available

        // tear down this watcher as you now no longer need it
        unwatch()
      })
    }
  }
}

【讨论】:

    【解决方案2】:

    我能想到的方法至少有两种:

    1。依赖注入

    父/根到provide组件树下的数据/对象和接收端(子)到inject它们。

    家长

    {
      data: () => ({
        userInfo: null
      }),
    
      created() {
        // Populates data via Firebase 
        // Code omitted for brevity...
    
        this.userInfo = doc.data();
      },
    
      provide() {
        return Object.defineProperty({}, 'userInfo', {
          get: () => this.userInfo
        })
      }
    }
    

    儿童

    // Children
    {
      inject: ['userInfo'],
    
      created() {
        if (this.userInfo) {
          // Do something 
        }
      },
      mounted() {
        if (this.userInfo) {
          // Do something 
        }
      },
      // Any lifecycle hooks, methods or virtually anywhere needing a reference to this object
    }
    

    2。 Vuex 商店

    而不是传递道具并不得不处理可能的异步问题;您可以让 Vuex 为您同步状态,而不必担心数据是 nullundefined

    商店

    export default {
      state: {
        userInfo: null
      },
      mutations: {
        UPDATE_USER(state, payload) {
          state.userInfo = payload;
        }
      }
    }
    

    父/App.js

    export default {
      created() {
        // On firebase success callback
        this.$store.commit('UPDATE_USER', doc.data());
      }
    }
    

    儿童

    import { mapState } from 'vuex';
    
    export default {
      computed: {
        ...mapState([
          'userInfo'
        ])
      },
    
      created() { },
      mounted() { },
      methods() { 
        if (this.userInfo) {
          // Do something safely
        }
      },
      // Anywhere
    }
    

    【讨论】:

      【解决方案3】:

      正如 cmets 中已经提到的,您遇到了 Promise 解决问题。当通过 Promise 将结果作为 Prop 传递时,该结果基本上是空的,最终会导致您的子组件 created() 钩子出现错误。

      我建议您使用v-if,就像您在子组件本身中所做的那样。但是,我会在父组件中使用v-if 来简单地显示/隐藏子组件。这不仅应该解决您的 created() 挂钩中的问题,而且还有助于在您的子组件中建立一个更具可读性的标记,您可以在其中删除所有 v-ifs。

      所以让我们假设你有这样的父/子关系。

      <SomeParentComponent>
        <SomeChildComponent :someProp="yourProp" />
      </SomeParentComponent>
      

      在解析 Promise 后设置 yourProp 时,初始值将是 nullundefined 或您设置的任何值。为避免传入一个空的 prop,您可以将其包装在 v-if 中,其表达式的计算结果为 yourProp,就像这样。

      <SomeParentComponent>
        <SomeChildComponent :someProp="yourProp" v-if="yourProp" />
      </SomeParentComponent>                           <!-- ^-- See here -->
      

      这样做,只有在设置yourProp 时才会呈现子级,因此您不会再收到任何错误。

      【讨论】:

      • 啊,太好了!谢谢@Aer0,它工作得很好!这对加载速度有什么影响。我想这取决于获取初始数据/更新子项需要多长时间?另外,以后有没有更好的方法来避免这种情况?
      • 您建议使用v-if 方向,当异步数据可用于下面的组件时,这将创建一个跳跃行为。
      • 没错。如果您将 Promise 作为 Prop 传递,您可以等待 Promise 以 async 的方式解决。此外,您应该以不同的方式注意神经质的行为。如果你的数据依赖于你的 Prop 才能正确显示,即使使用默认值,你也会得到一个跳跃的行为。
      【解决方案4】:

      created 钩子中应该有道具。假设这些值是同步的。尝试添加default 值,以删除不必要的v-if 指令。这应该可以解决同步和异步数据的问题。

      {
        props: {
          length: {
            type: Number,
            default: 0,
          },
        },
      }
      

      【讨论】:

      • 如果一个 prop 是 Promise 怎么办?我认为这就是问题所在。
      • 我刚刚编辑了我的答案。在这种情况下,它们肯定不会立即可用。
      • 尽管它有帮助并且应该是设置默认值的最佳实践,但它仍然不是问题的答案。如果您依赖于在 created() 挂钩期间正确设置的值,则必须确保在创建组件期间设置它或使用其他依赖 Pop 的方式。
      • @Aer0 这是不对的。如果数据不存在,您可能会有特定的行为。此外,您所说的当然不是最佳做法。
      猜你喜欢
      • 1970-01-01
      • 2019-05-30
      • 2021-08-21
      • 1970-01-01
      • 2021-09-08
      • 2018-01-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多