LoginSignup
2
1

More than 1 year has passed since last update.

Spreadjsの初期化で学ぶ、React非同期処理

Last updated at Posted at 2022-01-25

何がしたいか

エクセルライクなスプレッドをWebアプリに組み込めるライブラリSpreadjsがあります。
その初期化処理中はローディングを出して、初期化が終わったらスプレットを出す実装の中で、
非同期の概念が少し理解できたので、まとめてみました。

理解できないがとりあえず実現できたコード

import GC from '@grapecity/spread-sheets'
import { Column, SpreadSheets, Worksheet } from '@grapecity/spread-sheets-react'
import '@grapecity/spread-sheets-resources-ja'
import { useState } from "react"

export const TestSheet = () => {
    const [isLoading, setIsLoading] = useState(true)

    const initSpread = (spread: GC.Spread.Sheets.Workbook) => {
        return new Promise<void>(resolve => {
            setTimeout(() => {
                // 3秒の重い処理を想定
                resolve()
            }, 3000) 
        })
    }

    return (
        <>
            {isLoading ? <h1>Loading....</h1> : <></>}
            <div style={{ visibility: isLoading ? 'hidden' : 'visible' }}>
                <SpreadSheets workbookInitialized={spread => {
                    initSpread(spread).then(() => setIsLoading(false))
                }}>
                    <Worksheet >
                        <Column />
                    </Worksheet>
                </SpreadSheets>
            </div>
        </>
    )
}

非同期難しい

残念ながら、私はこのコードがなぜ上手くいくのか、理解できませんでした。
Promiseじゃなくて、async/awaitを使用したいし、setTimeoutも不要だと思ってました。
そして、色々消したり、付け加えたりしたのですが、うまくいきませんでした。
そこで、書籍を読み漁りました。

読み漁りの結果

プログラミング TypeScript スケールするJavaScript アプリケーション開発
上記書籍より、少しイメージがつかめたので説明します。

ローディングのstateを変更するタイミング

重い処理が終わったタイミングでstateを変更すればよいので、
シンプルに重い処理の後に記述すればよく、.then()とか、awaitは不要です。

 const initSpread = (spread: GC.Spread.Sheets.Workbook) => {
        return new Promise<void>(resolve => {
            setTimeout(() => {
                // 3秒の重い処理を想定
                setIsLoading(false) //new !!
                resolve()
            }, 3000) 
        })
    }

//省略
    return (
                <SpreadSheets workbookInitialized={spread => {
                    initSpread(spread) //.then(() => setIsLoading(false))
                }}>
    )

Promiseやasyncが非同期を生み出すという勘違い

Promiseや、そのシンタックスシュガーであるasyncを付けるだけで非同期にはなりません。
非同期は、JavaScriptプラットフォームによって提供されているネイティブな非同期APIを呼び出す必要があります。

今回は、setTimeout()で非同期APIを呼び出しています。
そして、非同期APIを呼び出せば、その時点で、呼び出している関数(initSpread)も非同期になります。
よって、Promiseやasyncは不要です。

 const initSpread = (spread: GC.Spread.Sheets.Workbook) => {
        setTimeout(() => {
            // 3秒の重い処理を想定
            setIsLoading(false)
        }, 3000) 
    }

待つ必要がなければ、await不要→asyncも不要

initSpreadはバックグラウンドで勝手に処理されて、最後にloadingのstateをオフにすればよいので、
待つ必要はありません。asyncはawaitを書く際に必要になるものなので、
待つ必要がなければ、何も記述する必要はありません。
非同期処理がただ駆け抜けるだけです。

まとめ

ただでさえ、非同期がわかっていないのに、Reactのレンダリングするタイミングも相まって、
非同期の理解がなかなか進みませんでした。
レンダリング内でawaitすれば、レンダリングが止まるのも、当たり前だと理解できるようになりました。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1