Electronのアプリ開発中にいくつか勘違いしたり,よく分からなかったり,調べたことをまとめた.
React
textboxの値を取得する
取得には,stateかrefを使う.単に入力されたテキストを取得したいなら,refのようが良さそう.refを使えばノードの参照を得られる
class Content extends React.Component<Record<string, unknown>> {
searchBoxRef: RefObject<HTMLInputElement>;
constructor(props: Record<string, unknown>) {
super(props);
this.searchBoxRef = createRef();
}
render() {
return <>
<input ref={this.searchBoxRef} />
</>;
}
// this.searchBoxRef.current!.value で入力された値が取れる
}stateを使う場合は,onChangeで変更のたびにstateを更新する.
forwardRefの型
forwardRefを使った例.<select>で選んだ値を取得するためにrefを使っている.
import React, { ForwardedRef, ForwardRefRenderFunction } from "react";
type Props = JSX.IntrinsicElements["select"];
const element: ForwardRefRenderFunction<HTMLSelectElement, Props> = ({ /* */ }: Props, ref: ForwardedRef<HTMLSelectElement>) => {
return (
<select ref={ref}>
/* options */
</select>
);
};
const Selector = React.forwardRef<HTMLSelectElement, Props>(element);
export default Selector;普通の関数コンポーネントだとエラーが出るので,ForwardRefRenderFunctionにする必要がある.
Spectron
テストでメインプロセスにメッセージを送る
import { Application } from 'spectron';
app = new Application(/* applicationConfig */);app.electronは,Electron.RemovetMainInterface型である.この型にはipcMainは存在するがipcRendererは存在しない.
しかし,Spectronのドキュメントには,
The
electronproperty is your gateway to accessing the full Electron API.
と書いてある.
app.electron.ipcRenderer.send(/* message */, /* value */));と書くと,型的にはipcRendererがないのでエラーが出るが,実行すると動く.
テストでメインプロセスにメッセージを送りたいときは,どうするのが正しいんだろう?
lowdb
値の更新が反映されない
レンダラープロセスで値を更新した後にメインプロセスで値を取得すると,変更前の値が返ってくる.
雑な解決法
adapter = new FileSync<型>(ファイル);
db = low(adapter)上記のコードが書かれたファイルをメインプロセスとレンダラープロセスでインポートしており,dbが複数作成されていた.ファイルが変更された後にnew FileSyncをし直して値を読み込むと,ちゃんと変更後の値になっていた.
nodeIntegration: trueにして,何でもかんでもレンダラープロセスから呼び出すようにしているのがそもそも良くない.
Typescript / JavaScript
メソッドの抽出をしたらテストが実行されない
非同期のメソッドapi.get()のテストがしたいので,以下のようなテストを書いた.
description('APIのテスト', () => {
it('get', async () => {
const res = await api.get();
// 処理
assert.strictEqual(/* 条件 */);
});
});別のテストと処理が重複していたので,共通部分を関数に抽出した.
async function syori(){
const res = await api.get();
// 処理
return ...
}
description('APIのテスト', () => {
it('get', async () => {
const syori_result = syori();
assert.strictEqual(/* 条件 */)
});
});その結果,syoriが実行されなくなり,テストが常に成功するようになった.
原因
抽出した関数は非同期なので,awaitが必要だった(またはdoneを使う).何も考えずに抽出したらダメだった.
description('APIのテスト', () => {
it('get', async () => {
const syori_result = await syori(); // await してなかった
assert.strictEqual(/* 条件 */)
});
});Array.findが常に一番目の値を返す
以下のようなコードを書くと,常にxsの一番目の値が返ってくる.
xs.find(async (x) => await x.get())原因
非同期関数はPromiseを返すので,常に真.そりゃそうだ.,「awaitで中身を取り出す」みたいな印象があるらしい.
結局はPromise.allを使った.
感想
- 非同期関数に関しては,理解せずにコピペしています,みたいな酷い間違いだった.長時間ハマっていたわけではないけど,そんなコードを書いてしまうのは良くない.
- とりあえず
nodeIntegration: trueにして開発しているけど,それのせいで複雑になっている気がする - Electronのe2eテストは
awaitだらけになるので,どうにかしたくなった- 書き方が悪いのかフレーキーテストで困る