VUE JS中“ reversedList”计算属性中的意外副作用

时间:2019-06-26 21:49:23

标签: javascript vuejs2

在我的应用程序中,我试图避免直接更改道具,但是我在终端中始终遇到此错误:

Unexpected side effect in "reversedList" computed property

然后在控制台中我得到:

Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "List"

我的组件代码是:

 <template>
  <div class="mb-5 mt-5 container">
    <div class="list-group">
      <a href="#" class="list-group-item list-group-item-action active">
        My List
        <span class="badge badge-light">{{List.length}}</span>
      </a>
      <a
        href="#"
        class="list-group-item list-group-item-action"
        v-for="(result, index) in reversedList"
        :key="index"
      >
        {{result.collectionName}}
        <br>
        <b>{{result.artistName}}</b>
        <br>
        <div class="row">
          <div class="col-md-6">
            <button
              class="btn btn-success btn-sm btn-block"
              v-on:click="removeElement(index)"
            >Remove</button>
          </div>
          <div class="col-md-6">
            <button
              class="btn btn-info btn-sm btn-block"
              @click="toiTunesAlbum(result)"
            >View on iTunes</button>
          </div>
        </div>
      </a>
    </div>

    <button class="btn btn-info mt-5 btn-lg btn-block" @click="saveList()">Save</button>
  </div>
</template>

<script>
export default {
  name: "List",
  props: ["List"],
//   data: function() {
//     return {
//       result: ["List"],
//     };
//   },
data() {
    return {
      // Define a reversed data property
      reversed: false,
    };
  },

  mounted() {
    if (localStorage.getItem("List")) {
      try {
        this.List = JSON.parse(localStorage.getItem("List"));
      } catch (err) {
        /* eslint-disable no-console */

        console.error(err);
        /* eslint-disable no-console */
      }
    }
  },


computed: {
    reversedList() {
      // Check if we need to reverse the list
      if (this.reversed) {
        return this.List.reverse();
      } else {
        // If not, return the plain list passed in
        return this.List;
      }
    }
  },


  methods: {
    saveList() {
      let parsed = JSON.stringify(this.List);
      localStorage.setItem("List", parsed);
              /* eslint-disable no-console */

        console.log(this.List);
        /* eslint-disable no-console */
    },

    removeElement: function(index) {
      this.List.splice(index, 1);
    },

    resizeArtworkUrl(result) {
      return result.artworkUrl100.replace("100x100", "160x160");
    },
    toiTunesAlbum(result) {
      window.open(result.collectionViewUrl, "_blank");
    }
  }
};
</script>

<style scoped>
.album-cover {
  width: 100%;
  height: auto;
  background-color: aqua;
}

.album-container {
  height: 350px;
}
</style>

父组件

<template>
  <div class="container search">
    <div class="row">
      <div class="col-md-8">

 <div class="jumbotron mt-5" style="clear:both">
      <h1 class="display-4">{{title}}</h1>
      <p class="lead">{{intro}}</p>
      <hr class="my-4">
      <p v-if="validated" :class="errorTextClass">Enter a valid search term</p>

      <button
        type="button"
        class="btn btn-primary btn-lg mb-3"
        v-on:click="refreshPage"
        v-if="result.length > 1"
      >
        <font-awesome-icon icon="redo"/> Start again
      </button>
      <input
        class="form-control form-control-lg mb-3"
        type="search"
        placeholder="Search"
        aria-label="Search"
        v-model="search"
        required
        autocomplete="off"
        id="search"
      >

      <div v-for="(result, index) in result" :key="index">
        <div class="media mb-4">
          <img
            :src="resizeArtworkUrl(result)"
            alt="Album Cover"
            class="album-cover align-self-start mr-3"
          >
          <div class="media-body">
            <h4 class="mt-0">
              <!-- <button
                type="button"
                class="btn btn-primary btn-lg mb-3 float-right"
                v-on:click="addItem(result)"
              >
                <font-awesome-icon icon="plus"/>
              </button>-->

              <button
                type="button"
                class="btn btn-primary btn-lg mb-3 float-right"
                v-on:click="addItem(result)"
                :disabled="result.disableButton"
              >

                <font-awesome-icon icon="plus"/>
              </button>

              <b>{{result.collectionName}}</b>
            </h4>
            <h6 class="mt-0">{{result.artistName}}</h6>
            <p class="mt-0">{{result.primaryGenreName}}</p>
          </div>
        </div>
      </div>

      <div :class="loadingClass" v-if="loading"></div>

      <button
        class="btn btn-success btn-lg btn-block mb-3"
        type="submit"
        v-on:click="getData"
        v-if="result.length < 1"
      >
        <font-awesome-icon icon="search"/>Search
      </button>
    </div>



      </div>

      <div class="col-md-4">


    <List :List="List"/>


      </div>


    </div>
    <!-- <div class='div' v-bind:class="[isActive ? 'red' : 'blue']" @click="toggleClass()"></div> -->


  </div>
</template>

<script>
import List from "../components/myList.vue";

export default {
  name: "Hero",
  components: {
    List
  },
  data: function() {
    return {
      title: "Simple Search",
      isActive: true,
      intro: "This is a simple hero unit, a simple jumbotron-style.",
      subintro:
        "It uses utility classes for typography and spacing to space content out.",
      result: [],
      errors: [],
      List: [],
      search: "",
      loading: "",
      message: false,
      isValidationAllowed: false,
      loadingClass: "loading",
      errorTextClass: "error-text",
      disableButton: false
    };
  },

  watch: {
    search: function(val) {
      if (!val) {
        this.result = [];
      }
    }
  },

  computed: {
    validated() {
      return this.isValidationAllowed && !this.search;
    },
    isDisabled: function() {
      return !this.terms;
    }
  },

  methods: {
    getData: function() {
      this.isValidationAllowed = true;
      this.loading = true;
      fetch(`https://itunes.apple.com/search?term=${this.search}&entity=album`)
        .then(response => response.json())
        .then(data => {
          this.result = data.results;
          this.loading = false;
          /* eslint-disable no-console */
          console.log(data);
          /* eslint-disable no-console */
        });
    },

    toggleClass: function() {
      // Check value
      if (this.isActive) {
        this.isActive = false;
      } else {
        this.isActive = true;
      }
    },

    refreshPage: function() {
      this.search = "";
    },
    addItem: function(result) {
      result.disableButton = true; // Or result['disableButton'] = true;
      this.List.push(result);
      /* eslint-disable no-console */
      console.log(result);
      /* eslint-disable no-console */
    },

    resizeArtworkUrl(result) {
      return result.artworkUrl100.replace("100x100", "160x160");
    },
    toiTunesAlbum (result) {
        window.open(result.collectionViewUrl, '_blank')
      }
  }
};


</script>

<style>
.loading {
  background-image: url("../assets/Rolling-1s-42px.gif");
  background-repeat: no-repeat;
  height: 50px;
  width: 50px;
  margin: 15px;
  margin-left: auto;
  margin-right: auto;
}

.error-text {
  color: red;
}

.media {
  text-align: left;
}

.album-cover {
  width: 80px;
  height: auto;
}

.red {
  background: red;
}

.blue {
  background: blue;
}

.div {
  width: 100px;
  height: 100px;
  display: inline-block;
  border: 1px solid black;
}
</style>

我收到主控制台错误,然后为reversedList添加了一些代码,但是没有用吗?有任何想法吗。这些值来自父组件,该组件通过prop传递到该组件。

2 个答案:

答案 0 :(得分:1)

问题在于,这会改变原始列表:

return this.List.reverse();

您需要对副本进行复印:

return [...this.List].reverse();

在这里更改道具也有问题:

this.List = JSON.parse(localStorage.getItem("List"));

在这里:

this.List.splice(index, 1);

在两种情况下,如果您想使用道具,都需要将此责任移交给父母。

答案 1 :(得分:0)

问题是您正在使用splice

this.List.splice(index, 1);

  

splice()方法通过删除或替换现有元素和/或在适当位置添加新元素来更改数组的内容。

因此,splice()方法突变 this.List

相反,您可以像这样返回新列表:

removeElement: function(index) {
  return this.List.slice().splice(index, 1);
},

Slice将复制this.List

类似地,避免将this.List直接更改为reverse()