如何在Vuex中更新一个依赖于另一个的数组

时间:2019-08-12 19:12:52

标签: vue.js vuex

我有一个搜索字段,我想根据它过滤用户。 我向用户显示是否没有过滤器,或者如果您搜索任何内容,则显示filteredUsers

问题在于,如果您在搜索栏中进行搜索,然后我尝试编辑一个字段(内联编辑),则会users被更新,而filteredUsers不会被更新。

我还有其他过滤器和操作,这就是为什么我只发布了那部分代码。我必须同时使用它们,因为在其他情况下,我需要用户。

vue.js中如何工作?

//in componnet 
computed: {
      ...mapGetters([
        'users',
        'filteredUsers'
      ]),
      getFilteredUsers () {
        return this.filteredUsers || this.users
      }
    }

// in mutation
   UPDATE_CONSUMER (state, { user, index }) {
       // here users is updated, but filteredUsers in not
        state.users.splice(index, 1, user)
  },
<tr v-for="user, index in getFilteredUsers">

// getters.js file
export default {
  users: state => {
    return state.users
  },
  filteredUsers: state => {
    return state.filteredUsers
  }
}

// mutations.js
SEARCH_USERS (state) {
    const searchTextTrimmed = state.searchText.trimStart()

    const users = [
      ...state.users.filter(user => {
        return user.name.toLowerCase().startsWith(searchTextTrimmed.toLowerCase())
      })
    ]
    if (searchTextTrimmed) {
      state.filteredUser = users
    } else {
      state.users = users
      state.filteredUser = null
    }
  }

1 个答案:

答案 0 :(得分:1)

理想情况下,state中不会同时包含两个数组。相反,您只有userssearchText。然后,您将在filteredUsersgetters派生的users中有searchText

在下面的示例中,我尝试保留问题中的UPDATE_CONSUMER突变。我从那里推断,假设您要在编辑用户对象时创建它们的副本,而不是变异原始对象。如果可以更改原始对象,因为这两个对象在两个列表之间共享,那么整个问题将变得毫无意义。

const store = new Vuex.Store({
  state: {
    searchText: '',
    
    users: [
      {name: 'Black', fruit: 'Apple'},
      {name: 'Blue', fruit: 'Pear'},
      {name: 'Brown', fruit: 'Banana'}
    ]
  },
  
  mutations: {
    UPDATE_CONSUMER (state, { user, index }) {
      state.users.splice(index, 1, user)
    },

    UPDATE_SEARCH_TEXT (state, searchText) {
      state.searchText = searchText
    }
  },
  
  getters: {
    filteredUsers (state) {
      const searchText = state.searchText.trimStart().toLowerCase()
      const users = state.users
      
      if (!searchText) {
        return users
      }

      return users.filter(user => user.name.toLowerCase().startsWith(searchText))
    }
  }
})

new Vue({
  el: '#app',
  store,
  
  computed: {
    ...Vuex.mapState(['users']),
    ...Vuex.mapGetters(['filteredUsers']),
    searchText: {
      get () {
        return this.$store.state.searchText
      },
      set (searchText) {
        this.$store.commit('UPDATE_SEARCH_TEXT', searchText)
      }
    }
  },
  
  methods: {
    onInputFruit (user, fruit) {
      const newUser = {...user, fruit}
      const index = this.users.indexOf(user)
      
      this.$store.commit('UPDATE_CONSUMER', {user: newUser, index})
    }
  }
})
#app > * {
  margin: 0 0 10px;
}

table {
  border-collapse: collapse;
}

td, th {
  background: #eee;
  border: 1px solid #777;
  padding: 5px;
}
<script src="https://unpkg.com/vue@2.6.10/dist/vue.js"></script>
<script src="https://unpkg.com/vuex@3.1.1/dist/vuex.js"></script>

<div id="app">
  <input v-model="searchText">
  <table>
    <tr><th>Name</th><th>Fruit</th><tr>
    <tr v-for="user in filteredUsers" :key="user.name">
      <td>{{ user.name }}</td>
      <td>
        <input :value="user.fruit" @input="onInputFruit(user, $event.target.value)">
      </td>
    </tr>
  </table>
  <p>{{ filteredUsers }}</p>
  <p>{{ users }}</p>
</div>

其他一些注意事项:

  1. 我摆脱了users的使用方法,而是使用了mapState。如果您希望通过mapGetters进行所有操作,则可以将其恢复原样。
  2. 您不需要getFilteredUsers。而是让filteredUsers始终返回符合搜索条件的当前用户列表。我提供了一种优化措施,如果没有searchText,它会尽早返回,但是如果没有它,它仍然可以工作。
  3. 在您的代码中,您有const users = [...state.users.filter。无需使用[...]在此处创建新数组,因为filter仍将返回新数组。
  4. 我将searchText和过滤保留在存储中,但是感觉有点像应该由组件状态来处理。在不了解您的应用程序的情况下很难说,但是搜索框可能只存在于UI的一小部分,并且不一定需要通过商店与其他应用程序共享。

我实现此方法的一个潜在问题是,随着用户键入数据会立即更新。在我的示例中,这不是问题,但是如果根据正在编辑的字段过滤列表,则可能导致该行在用户键入时突然消失。避免该问题的一种方法是在更新数据之前等待场模糊,例如使用change事件而不是input事件。可以使用其他变通办法,具体取决于预期的行为。