Daily/2021/01/28

2021-01-28

Todo

Todo Done
React で text input を扱うときの実装を考える o
protoc が何故か go を吐き出さない問題 o
mustEmbedUnimplemented** について調べる

Log

React で text input を扱うときの実装を考える

input の Component を作ろうとしたときにどう作ればいいんだ問題。

イメージ的には、外から使う分にはこんな感じにするのが良いかなって。CreateUserForm が form の中の入力内容を state で持つ感じ。 Submit したときの動作を上から渡す。API 叩くのか、上流で値を受け取るのかなど。

<CreateUserForm onSubmit={(values: FormComponentValues) => result} />

で、CreateUserForm の Component がこう。あくまでイメージ

<form onSubmit={() => props.onSubmit(formState)}>
  <CustomInput />
  <CustomInput />
  <CustomInput />
  <Submit />
</form>

ただこれだと入力したイベントとかは外から渡せないが・・・Form の値を管理する責務を Form で Wrap するならこうなのかしら。。

まず React の form に関する記載はここ。

https://ja.reactjs.org/docs/forms.html

onChange を渡しておいて、 event.target.value を取れば良いみたい。

change event はここ。

https://developer.mozilla.org/ja/docs/Web/API/HTMLElement/change_event

ここだけ気になる。

など一部の要素では、コントロールがフォーカスを失うまで change イベントが発生しません。以下のフィールドに何かを入力してから、他の部分をクリックするとイベントが発生します。

フォーカスを失うまで change イベントが発生しないなら、なんで onChange で value={props.value} が機能するんだろうか。

React の form のページにはこう。

handleChange はキーストロークごとに実行されて React の state を更新するので、表示される値はユーザがタイプするたびに更新されます。

試しに実装したところ、キーストローク毎に実行された。React で扱う input は HTML としての input と少し挙動が違うっぽい。

textarea とかもちょっと違う旨が書いてある。

React では、 は代わりに value 属性を使用します。こうすることで、 を使用するフォームは単一行の入力フォームと非常に似た書き方ができるようになります

使いやすいようになってるみたい。

protoc が何故か go を吐き出さない問題

吐き出されないと当然開発に入れないので困っている

以前書いた ProtocolBuffer の import の記事 で動作を確認しようと思ったら、よく見たらなんかおかしい。

protoc コマンドのオプション --go_out=plugins=grpc:./go が記載してる protoc のバージョン(3.6.0)と一致してない・・・・

たぶん別のバージョンの protoc で動かしたんだと思うので、再度上から実行して書き直した。

すると、

protoc -I . -I ~/src --go-grpc_out ./go models/user.proto

ここでやっぱり user_grpc.pb.go が生成されない。もしかして service がないと生成されなくなってる?

試しに service を追加したら生成された。

というかよく生成されるコード読んでみたら、message に対応する type が生成されてないぞなんだこれ。

で、解決。そもそも、protoc のバージョンを変えたら以下のエラーが出るようになってた。

--go_out: protoc-gen-go: plugins are not supported; use 'protoc --go-grpc_out=...' to generate gRPC

ので、--go_out=plugins=grpc:./go をとりあえず以下のようなオプションにした。

--go-grpc_out <dir>

この書き方をすると protoc-gen-go-grpc が動いているという認識がそもそも足りなかったのだけど、 protoc-gen-go-grpcmessage に対応する struct を生成しない。

その struct の定義自体は grpc じゃないもんね。確かに。

今までもたぶん message に対応する struct を生成してくれていたのは protoc-gen-go

なので、正しくアウトプットするためには、

--go_out <dir> --go-grpc_out <dir>

というオプションが必要だった。勉強になった。

mustEmbedUnimplemented

昨日始めた時間がもう今日だったので、続きに書く。

無事 protoc がバージョンできてよかった、と思ったのもつかの間、 mustEmbedUnimplementedXXXXXServer というメソッドの実装がないよ、というエラーが出た。

調べたところ、同じような issue がこれ。

https://github.com/grpc/grpc-go/issues/3794

そのなかに出てくる長い長い議論がこれ。

https://github.com/grpc/grpc-go/issues/3669

ちょっと読めないので、ソースコードなどをかいつまんで見てみると、UnimplementedServer を embeded すれば良いみたい。

以前書いた ProtocolBuffer の import の記事 で生成した Server を例にすると、

まず、こういう interface が生成される。

// UserServiceServer is the server API for UserService service.
// All implementations must embed UnimplementedUserServiceServer
// for forward compatibility
type UserServiceServer interface {
  GetUser(context.Context, *GetUserMessage) (*GetUserResponse, error)
  mustEmbedUnimplementedUserServiceServer()
}

問題になっているのは mustEmbedUnimplementedUserServiceServer で、どうやら UnimplementedUserServiceServer を Embed させるための func っぽい?

UnimplementedUserServiceServer を見てみる。

// UnimplementedUserServiceServer must be embedded to have forward compatible implementations.
type UnimplementedUserServiceServer struct {
}

func (UnimplementedUserServiceServer) GetUser(context.Context, *GetUserMessage) (*GetUserResponse, error) {
  return nil, status.Errorf(codes.Unimplemented, "method GetUser not implemented")
}
func (UnimplementedUserServiceServer) mustEmbedUnimplementedUserServiceServer() {}

何もしない func が入っている。 コメントにもある通り、上位互換性を保つために実装に Embed しないといけないらしい。

つまり、protobuf 側に新しい rpc が生えたときに、UnimplementedUserServiceServer を Embed しておくと、実装をしなくても、自動で codes.Unimplemented を返却するメソッドを生やすよ、ということではないかと思う。

ちなみに Embed する実装はこう。

type server struct {
    pb.UnimplementedUserServiceServer
}

そもそもこの func いらないよ!ってときは、go-grpc の option でなくせるらしい。やってない。

--go-grpc_opt=require_unimplemented_servers=false

上のキーワードで調べたらめっちゃわかりやすい note あった。ありがとうございます。

https://note.com/dd_techblog/n/nb8b925d21118

その他作業中に発生したこと

protoc の plugin についての記事

どっかで読んで作ってみたい

https://qiita.com/yugui/items/87d00d77dee159e74886

新しいバージョンの protoc で生成した protobuf を元々あったアプリに読み込ませたらエラー

mustEmbedUnimplemented*** という func がないエラーがドバーッと。

なんぞこれ?という同じ疑問の issue があがってたので、明日以降調べる。

https://github.com/grpc/grpc-go/issues/3794

Next


logs

Satoshi Koizumi

Daily/2021/01/31

Daily/2021/01/24