如何在Vue 3中使用组合 API,在子组件中的某个事件上重新渲染父组件。

huangapple go评论53阅读模式
英文:

How to re-render parent component on certain event in child component in vue 3 using composition API

问题

我可以实际使用“选项 API”来完成,但现在我正在使用“组合 API”与“可组合项”,而且我卡住了。整天在网上搜索,但没有找到答案。
所以,我有两个组件(members.vuememberStore.vue)。我的目标是当我在memberStore.vue中发布一个新成员(我在members.vue中加载它时),该父组件中的成员列表应自动重新渲染。

以下是这些页面的内容:

members.vue

<template>
  <div>
    <memberStore></memberStore>
    <table>
      <thead>
        <tr>
          <th><span>Name</span></th>
          <th><span>Address</span></th>
          <th></th>
        </tr>
      </thead>
      <tbody>
        <template v-for="item in members" :key="item.id">
          <tr>
            <td>{{ item.name }}</td>
            <td>{{ item.address }}</td>
          </tr>
        </template>
      </tbody>
    </table>
  </div>
</template>

<script>
import useMembers from "../../composables/members";
import { onMounted } from "vue";
import memberStore from "../../components/members/memberStore.vue";

export default {
  components: { memberStore },
  setup() {
    const { members, getMembers } = useMembers();

    onMounted(getMembers);

    return {
      members,
    };
  },
};
</script>

memberStore.vue

<template>
  <div>
    <form @submit.prevent="saveMember">
      <div>
        <div><label for="name">Name</label></div>
        <div><input type="text" name="name" id="name" v-model="form.name"></div>
        <div><label for="address">Address</label></div>
        <div><input type="text" name="address" id="address" v-model="form.address"></div>
      </div>
      <div>
        <button type="submit">Save</button>
      </div>
    </form>
  </div>
</template>

<script>
import { reactive } from "vue";
import useMembers from "../../composables/members";

export default {
  setup() {
    const form = reactive({
      name: "",
      address: "",
    });

    const { errors, storeMember } = useMembers();

    const saveMember = async () => {
      await storeMember({ ...form });
    };

    return {
      form,
      errors,
      saveMember,
    };
  },
};
</script>

src/composables/members.js

import { ref } from "vue";
import axios from "axios";
import { useRouter } from "vue-router";
import axiosClient from "../axios";

export default function useMembers() {
  const members = ref([]);
  const member = ref([]);
  const router = useRouter();
  const errors = ref("");

  const getMembers = async () => {
    let response = await axiosClient.get("/members");
    members.value = response.data.data;
  };

  const getMember = async (id) => {
    let response = await axiosClient.get("/members/" + id);
    member.value = response.data.data;
  };

  const storeMember = async (data) => {
    errors.value = "";
    try {
      await axiosClient.post("/members", data);
      // 添加以下行以手动重新获取成员列表
      await getMembers();
    } catch (e) {
      if (e.response.status === 422) {
        errors.value = e.error.response.data;
      }
    }
  };

  return {
    members,
    getMembers,
    router,
    member,
    getMember,
    storeMember,
  };
}

所有这些都按照我期望的方式工作。唯一的问题是我找不到自动重新渲染父组件的方法。

英文:

I can do it actually using option API, but now I'm using composition API with composables and I'm stuck. Have searched online all day but got no answer.
So, I have two components (members.vue and memberStore.vue). My goal is when I post a new member in memberStore.vue (which I load in members.vue), the member list in that parent component should be automatically re-rendered.

here are those pages:

members.vue

    &lt;template&gt;
     &lt;div&gt;
      &lt;memberStore&gt;&lt;/memberStore&gt;
       &lt;table &gt;
        &lt;thead&gt;
          &lt;tr&gt;
           &lt;th&gt;&lt;span&gt;Name&lt;/span&gt;&lt;/th&gt;
           &lt;th&gt;&lt;span&gt;Address&lt;/span&gt;&lt;/th&gt;
           &lt;th &gt;&lt;/th&gt;
          &lt;/tr&gt;
         &lt;/thead&gt;

       &lt;tbody &gt;
        &lt;template v-for=&quot;item in members&quot; :key=&quot;item.id&quot;&gt;
         &lt;tr&gt;
           &lt;td&gt; {{ item.name }} &lt;/td&gt;
           &lt;td&gt; {{ item.address }} &lt;/td&gt;
         &lt;/tr&gt;
        &lt;/template&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/template&gt;

&lt;script&gt;
 import useMembers from &quot;../../composables/members&quot;;
 import { onMounted } from &quot;vue&quot;;
 import memberStore from &quot;../../components/members/memberStore.vue&quot;;
 export default {
    components: { memberStore },
   setup() {
   const { members, getMembers } = useMembers();

   onMounted(getMembers);

   return {
     members,
   };
   },
 };
&lt;/script&gt;   

memberStore.vue

   &lt;template&gt;
    &lt;div&gt; 
        &lt;form @submit.prevent=&quot;saveMember&quot;&gt;
            &lt;div&gt;
                
                &lt;div&gt;&lt;label for=&quot;name&quot; &gt;Name&lt;/label&gt;&lt;/div&gt;
                    &lt;div &gt; &lt;input type=&quot;text&quot; name=&quot;name&quot; id=&quot;name&quot; v-model=&quot;form.name&quot; &gt;  &lt;/div&gt;
                &lt;div&gt; &lt;label for=&quot;address&quot;&gt;Address&lt;/label &gt; &lt;/div&gt;
                    &lt;div&gt;&lt;input type=&quot;text&quot; name=&quot;address&quot; id=&quot;address&quot; v-model=&quot;form.address&quot; &gt;&lt;/div&gt;
            &lt;/div&gt;
          &lt;div&gt;
            &lt;button type=&quot;submit&quot; &gt; Save&lt;/button&gt;
          &lt;/div&gt;
        &lt;/form&gt;
      &lt;/div&gt;
    &lt;/template&gt;

    &lt;script&gt;
    import { reactive } from &quot;vue&quot;;
    import useMembers from &quot;../../composables/members&quot;;
    export default {
        setup() {
        const form = reactive({
            name: &quot;&quot;,
            address: &quot;&quot;,
        });
    
        const { errors, storeMember } = useMembers();
    
        const saveMember = async () =&gt; {
            await storeMember({ ...form });
        };
    
        return {
            form,
            errors,
            saveMember,
        };
        },
    };
    &lt;/script&gt;

src/composables/members.js

   import { ref } from &quot;vue&quot;;
   import axios from &quot;axios&quot;;
   import { useRouter } from &quot;vue-router&quot;;
   import axiosClient from &quot;../axios&quot;;

   export default function useMembers() {
    const members = ref([]);
    const member = ref([]);
    const router = useRouter();
    const errors = ref(&quot;&quot;);

    const getMembers = async () =&gt; {
      let response = await axiosClient.get(&quot;/members&quot;);
      members.value = response.data.data;
    };

   const getMember = async (id) =&gt; {
     let response = await axiosClient.get(&quot;/members/&quot; + id);
     member.value = response.data.data;
   };

   const storeMember = async (data) =&gt; {
     errors.value = &quot;&quot;;
     try {
        await axiosClient.post(&quot;/members&quot;, data);
     } catch (e) {
       if (e.response.status === 422) {
          errors.value = e.error.response.data;
       }
     }
   };

    return {
     members,
     getMembers,
     router,
     member,
     getMember,
     storeMember,
   };
 }

All of these are working as I expected. The only problem is I can't find a way to auto re-render the parent component

答案1

得分: 1

memberStore.vue中,我添加了以下内容:

script

emits: ["customChange"],
setup(props, context) {
  ...
  const handleChange = () => {
    context.emit("customChange");
  };
  return {
    .... 
    handleChange,
  }; 
}

trigger

<form @submit.prevent="saveMember" @submit="handleChange">

members.vue

<memberStore @customChange="getmembers"></memberStore>
英文:

I solved it using emit event

In memberStore.vue I added the following:

script

emits: [&quot;customChange&quot;],
setup(props, context) {
...
const handleChange = () =&gt; {
  context.emit(&quot;customChange&quot;);
};
return {
  .... 
  handleChange,
 }; 
}

trigger

&lt;form @submit.prevent=&quot;saveMember&quot; @submit=&quot;handleChange&quot;&gt;

members.vue

 &lt;memberStore @customChange=&quot;getmembers&quot;&gt;&lt;/memberStore&gt;

答案2

得分: 0

在两个组件内部调用useMembers()会创建两个不同的members数组(以及useMembers内部创建的所有其他变量的多个实例)。

换句话说,members.vue中的membersmembersStore.vue中的members是独立且不同的。更新其中一个不会影响另一个。

您可以将members的定义移到useMembers函数之外,这样无论在哪里调用useMembers(),它都会引用相同的数组引用。

const members = ref([]); // 移动到useMembers函数之外

export default function useMembers() {
  const member = ref([]);
  const router = useRouter();
  const errors = ref("");

  ...

  return {
    members,
    ...
  }
}
英文:

calling useMembers() inside two components creates two different members arrays (as well as multiple of all other variables created within useMembers).

In other words, the members in members.vue is unique and different from the members in membersStore.vue. Updating one does not affect the other.

You can move your members definition above your useMembers function, and then it will be the same array reference wherever useMembers() is called

const members = ref([]); // moved outside of useMembers()

export default function useMembers() {
  const member = ref([]);
  const router = useRouter();
  const errors = ref(&quot;&quot;);
  ...

  return {
    members,
    ...
  }
}

huangapple
  • 本文由 发表于 2023年7月13日 11:08:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/76675641.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定