Vue3のCompositionAPIにて、親コンポーネント内の非同期で処理した結果を子コンポーネントに渡そうとしているのですが期待した結果にならないため質問いたします。
前提
テストコードのようにpathsとdataというプロパティを持ったオブジェクトをstatという名前で子コンポーネントに渡そうとしています。
※実際は渡したいプロパティが多いためオブジェクトとしてまとめて渡したい。
※Vuexを導入するほどの規模ではないのでなるべくプラグインを使わずに解決したい。
※allSettledでrejectされるとリクエストurlの情報が失われるためpathsとdataセットで渡したい。
環境
- vue: ^3.2.13
- ブラウザ:FirefoxおよびGoogleChromeにて確認
想定する動作
mount時もしくはボタンクリック時にfetchAPIを複数実行し、得られた結果をdataプロパティに格納する。getStatusが終了した時点でdataには結果が入っているので、stat.valueにはpathsとdataを持ったオブジェクトが代入されるはずである。
実際の動作
stat.valueをコンソール出力するとdataプロパティを確認できるが、その後にstat.value.pathsとstat.value.dataを出力すると前者のみ存在し、後者はundefinedと出力される。子コンポーネントで変更検知したタイミングでも同様の状態のまま渡っている。
質問したいこと
- Promise.allSettled().then()が終了した時点ですべてのPromiseは解決されているという認識でいいでしょうか。
- pathsが取得できてdataが取得できないということは非同期処理の結果が格納されていないと推察しますが、getStatusメソッドは非同期処理の完了を待たずに終了しているのでしょうか。
- dataに結果が格納されてから子コンポーネントにオブジェクトを渡すにはどういった方法をとる必要があるでしょうか。
テストコード
・Parent.vue
js
1<template> 2 <h3>Parent</h3> 3 <button @click="execute">取得</button> 4 <Child :stat="stat" /> 5 <div>{{ stat }}</div> 6</template> 7 8<script setup> 9import { ref, onMounted } from 'vue'; 10import Child from '@/components/Child.vue'; 11 12const stat = ref({}) //オブジェクトごと代入する想定でrefとしている 13 14onMounted(() => { 15 execute() 16}); 17 18const execute = () => { 19 const urls = [ 20 'https://httpbin.org/status/200', // "OK" 21 'https://httpbin.org/status/404', // "NG" 22 ]; 23 // 取得した結果をオブジェクトとしてstatに格納。 24 stat.value = getStatus(urls); 25 console.log('parent') 26 console.log(stat.value) // consoleではdata,paths共に結果が格納されている 27 // data: Array [ "OK", "NG" ] 28 // paths: Array [ "https://httpbin.org/status/200", "https://httpbin.org/status/404" ] 29 console.log(stat.value.paths) // Array [ "https://httpbin.org/status/200", "https://httpbin.org/status/404" ] 30 console.log(stat.value.data) // undefined 31 console.log('parent-end') 32} 33 34const getStatus = (urls) => { 35 const results = {}; 36 results['paths'] = urls; 37 const promises = urls.map(url => fetch(url)); 38 // ここで全てのPromiseを解決した値がほしい 39 Promise.allSettled(promises).then(resp => { 40 results['data'] = resp.map(r => { 41 console.log(r) 42 if (r.status === "fulfilled"){ 43 if(r.value.ok){ 44 return 'OK'; 45 } else { 46 return 'NG'; 47 } 48 } else { 49 return 'ERROR'; 50 } 51 }); 52 }); 53 return results; 54} 55</script>
・Child.vue
js
1<template> 2 <h4>Child</h4> 3 <div v-for="(path, i) in paths" :key="i"> 4 <div>{{ path }}:{{ data[i] }}</div> 5 </div> 6</template> 7 8<script setup> 9import { ref, toRef, watch } from 'vue'; 10 11const props = defineProps({ //eslint-disable-line 12 stat: { 13 type: Object, 14 required: true, 15 default: () => ({ 16 paths: [], 17 data: [], 18 }) 19 } 20}); 21 22const stat = toRef(props, 'stat'); 23const paths = ref([]); 24const data = ref([]); 25 26watch(props, () => { 27 console.log('child') 28 console.log(props.stat) // ->consoleではdata,paths共に結果が格納されている 29 // data: Array [ "OK", "NG" ] 30 // paths: Array [ "https://httpbin.org/status/200", "https://httpbin.org/status/404" ] 31 paths.value = stat.value.paths 32 data.value = stat.value.data 33 console.log(props.stat.paths) // ->Array [ "https://httpbin.org/status/200", "https://httpbin.org/status/404" ] 34 console.log(paths.value) // ->Array [ "https://httpbin.org/status/200", "https://httpbin.org/status/404" ] 35 console.log(props.stat.data) // ->undefined 36 console.log(data.value) // ->undefined 37 console.log('child-end') 38}) 39</script> 40
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。