💭
RFH(react-hook-form)のヘルパーを理解したい(get)
目的
react-hook-form
の実装内容を理解すること。
それに伴いコードリーディング力を上げること。
今回の対象
get.ts
import compact from './compact';
import isNullOrUndefined from './isNullOrUndefined';
import isObject from './isObject';
import isUndefined from './isUndefined';
export default <T>(obj: T, path?: string, defaultValue?: unknown): any => {
if (!path || !isObject(obj)) {
return defaultValue;
}
const result = compact(path.split(/[,[\].]+?/)).reduce(
(result, key) =>
isNullOrUndefined(result) ? result : result[key as keyof {}],
obj,
);
return isUndefined(result) || result === obj
? isUndefined(obj[path as keyof T])
? defaultValue
: obj[path as keyof T]
: result;
};
実際に読んでいく
get<T>(obj: T, path?: string, defaultValue?: unknown): any
まず、関数名がget
なので、何かしらを取得したいとわかります。
具体的には、obj
からpath
にマッチするものを取得したいのではないかと予想します。
返り値はanyなので具体的にはわかりません。
if (!path || !isObject(obj)) {
return defaultValue;
}
path
がない場合orobj
がObject型ではないときに、defaultValue
を返しています。
なんで<T>
に制約を持たせないんだろうか...?(誰かわかる方がいたらご教授ください)
const result = compact(path.split(/[,[\].]+?/)).reduce(
(result, key) =>
isNullOrUndefined(result) ? result : result[key as keyof {}],
obj,
);
path.split()
の引数に/[,[\].]+?/
という正規表現をとることにより、.
,,
,[
,]
の文字があったときにstringを分割し、配列に格納していきます。
+?
は、1つ以上の該当する文字が連続している場合にマッチするものだそうです。
この配列をcompact()
[1]に代入することにより、falsyが取り除かれた配列が得られます。
さらにこの配列をreduce()
します。コールバックの第一引数には累積値、第二引数には現在の値が入ってきます。(ちなみに、第三引数にはインデックス番号、第四引数には基の配列が入ってきます)
インデックス番号をi
とします。
-
i=0
の時、
result
には初期値のobj
が入り、key
には、配列の要素の1番目が入ります。
result
がnull
orundefined
の時は、result
自体を返却し、そうではない時はresult[配列の要素の0番目]
を返却します。 -
i=1
の時、
result
にはi=0
の時に返却された値が入り、key
には配列の要素の1番目が入ります。
そして、i=0
の時と同様の処理を行い、i=2,3,4,5...
という様に処理をしていきます。
example.ts
const obj = {
a: {
b: {
c: 'foo'
}
}
};
const path1 = 'a.b.c';
const path2 = 'a.b.d';
それぞれどうなるか考えてみてください。答えは一番下に書いておきます。
return isUndefined(result) || result === obj
? isUndefined(obj[path as keyof T])
? defaultValue
: obj[path as keyof T]
: result;
- まず、
isUndefined(result) || result === obj
により、result
がundefined
の場合、もしくはobj
と同じ場合に、次の評価に移ります。
違う場合は、result
が返却されます。(さっきの処理で探索が成功したということです) - 次に、isUndefined[2](obj[path as keyof T])により、
obj
のトップレベルに、key
に対応するvalue
があるかどうか調べます。value
があった場合はその値を返し、なかった場合はdefaultValue
を返して処理が終了します。
なぜ2.の処理をするをするかというと、パスがトップレベルのプロパティを指している場合(つまり、ネストされたプロパティがない場合)、reduce
は深く探索することなく終了するからです。
最後に
短いコードなのに難しかったです。reduce
とか三項演算子のネストとか色々ややこしい、、、
いっちょまえに問題出していますが、僕も記事を書きながら学んでおり、アウトプットのためにやっていることなので、悪しからず...
間違い等ありましたらご教授いただけると幸いです。
Discussion