Search code examples
vue.jscomponentsvue-componentvuexv-for

How to avoid rerendering all child components which are created by v-for directive


There is a list of child component

 <question-list-item
        v-for="(item, index) in questionListParsed"
        :key="item.id"
        :both-question="item"
        :class-id="classId"
        :subject-id="subjectId"
        :index="index+1"
      />

and the questionListParsed is a getter in vuex.

  /**************************************************************************
   * getters
   **************************************************************************/
  get questionListParsed(): QuestionListItemRes[] {
    const { questionList, showingOriginalQuestion } = this
    const questionListParsed = questionList.map((e) => {
      const recommendQuestion = e.recommendedQuestions[0]
      const recommendQuestionIds = showingOriginalQuestion[e.questionNumber]
      let arr = []
      if (recommendQuestionIds) {
        arr = recommendQuestionIds.filter((item) => {
          return !this.removedRecommendQuestionIds.includes(item)
        })
      }
      return {
        recommendQuestion: {
          ...recommendQuestion,
          stem: recommendQuestion.question,
          knowledges: splitMultiKnowledge(recommendQuestion.knowledge),
          questionSourceList: recommendQuestion.sources,
          categoryId: recommendQuestion.categoryId,
        },
        originalQuestion: {
          ...e,
          id: e.questionNumber,
          stem: e.question,
          difficulty: e.complexity,
          knowledges: splitMultiKnowledge(e.knowledge),
        },
        id: recommendQuestion.id, 
        questionSimilarId: e.questionNumber, 
        mistakeAnswerId: e.id,
        targetExerciseId: e.targetExerciseId,
        status: recommendQuestion.status,
      }
    })

    return questionListParsed
  }

and the questionListParsed is mainly depends on the state questionList whitch is the originnal data from server side. Now i change questionList by the following way

  @Mutation
  updateQuestionListByIndex(data: UpdateParams): void {
    if (data.value) {
      const temp = [...this.questionList]
      temp[data.index] = data.value
      this.questionList = temp
    }
  }

and commit the mutation inside an Action like these

        this.context.commit('updateQuestionListByIndex', {
          index: targetIndex,
          value: originQuestion[0],
        })

I just want to change one item in the array questionList and then questionListParsed changed. The expectation is that only one component updated but all of the child component updated(use console.log('updated') in its updated hocks).

How to do that?


Solution

  • The reason why all components are updated is because you use computed property (Vuex getters are Vue computed properties).

    Whenever anything in questionList is changed, questionListParsed is recomputed and because you are using map and generating new objects, the result is a new array with completely new objects --> every child in list is updated

    I would not consider it a problem because in reality only the DOM elements of the changed item are updated (that is the beauty of virtual DOM). If you do see some performance problem, the way around it is to stop using computed/getters and instead do the transformation only once when data is loaded and continue to work only with questionListParsed