🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

Q&A

解決済

1回答

1117閲覧

コンポーネントマウント時にuseEffect内でstate(URLをファイルオブジェクトに変換した値が入る配列型)を更新したい

kinoko8800

総合スコア16

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

0グッド

0クリップ

投稿2020/12/11 08:46

質問失礼致します。

困っていること
previousFilesで受け取ったURLをファイルをコンポーネントマウント時にファイルオブジェクトに変換して、stateを更新し、list表示させたいのですが、
state, oldFilesの更新がうまく行きません。添付した画像は,useEffect内でoldFilesをconsole.logしたものです。空配列かと思いきや、値はちゃんと入っているみたいなんですが、参照できないです。

いろいろと原因を探ってみましたが、まったく理解できないのでどなかヒントいただけると幸いです。

const FileUpload = React.memo(( { name, previousFiles, //ファイルのURLを要素にもつ配列 } ) => { const [oldFiles, setOldFiles] = useState([]); const oldFileCount = previousFiles && previousFiles.length > 0 ? previousFiles.length : 0; useEffect(() => { //URLからFileオブジェクトに変換 if (previousFiles && previousFiles.length > 0) { let filesArray = []; previousFiles.forEach((file, index) => { fetch(file) .then(response => response.blob()) .then(blob => { const str = file.split("/"); const fileName = str[str.length - 1]; const fileObj = new File([blob], fileName); fileObj.preview = URL.createObjectURL(fileObj); fileObj.path = fileName; filesArray[index] = fileObj }) }) setOldFiles(filesArray); //配列に格納したファイルオブジェクトをstateに } return () => { oldFiles.forEach(file => { URL.revokeObjectURL(file.preview) }) } }, [previousFiles]) useEffect(() => { console.log(oldFile) //上記のuseEffect内でstateを更新しても空配列が出力される ただ値は入っている } }, [oldFiles]) return ( //ファイルをリスト表示する ) }

useEffect内のconsole.logでoldFilesを出力した結果
イメージ説明

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

fetch() というのは非同期で実行されます。つまり結果を待たずに次の処理に移行します。この場合だと forEach() 内の fetch() は完了を待たずに次の fetch() に移り、最終的に setOldFiles(filesArray) にたどり着きます。この次点で fetch() が完了していなければ filesArray[] であり、上記のような結果になります。

では解決するか。解決方法は二つあります。一つは setOldFiles(filesArray)then() の中に入れてしまう方法です。

JavaScript

1... 2 fetch(file) 3 .then(response => response.blob()) 4 .then(blob => { 5 const str = file.split("/"); 6 const fileName = str[str.length - 1]; 7 const fileObj = new File([blob], fileName); 8 fileObj.preview = URL.createObjectURL(fileObj); 9 fileObj.path = fileName; 10 filesArray[index] = fileObj; 11 setOldFiles(filesArray); 12 }) 13...

これでもまあ動きますが、fetch() のたびに setOldFiles(filesArray) をするので効率が悪いです。

二つ目の方法は Promise.all() を使う方法です。これならば配列に対して一気に fetch() を実行でき、全ての処理が終わったらその結果を配列として集めることができます。そして setOldFiles(filesArray) をすればよいわけです。

投稿2020/12/13 11:56

A_kirisaki

総合スコア2853

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kinoko8800

2020/12/13 15:50

丁寧にご回答ありがとうございます! 処理の完了を待たずに、(thenの外側で)stateを更新をしていたから空配列になっていたんですね。 勝手に非同期処理の扱いを理解しているつもりでしたが甘かったです、、 アドバイス通り、実装して期待動作を確認できました! useEffect(() => { if (previousFiles && previousFiles.length > 0) { let filesArray = []; Promise.all(previousFiles.map(url => fetch(url) .then((response) => response.blob()) .then(blob => { const str = url.split("/"); const fileName = str[str.length - 1]; const fileObj = new File([blob], fileName); fileObj.preview = URL.createObjectURL(fileObj); filesArray.push(fileObj) }) )).then(() => { setOldFiles(filesArray) }) } return () => { oldFiles.forEach(file => { URL.revokeObjectURL(file.preview) }) } }, [previousFiles])
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問