条件が真のときだけ描画する
Simplify condition rendering in React.js ~ DEV Community ~
という記事で、条件が真のときだけ子ノードを描画するコンポーネントが紹介されています。次のようなものです。
const IF = ({ condition, children }) => {
if (!condition) return null;
return <>{children}</>;
};
単純に return children;
とせずに、Fragment で挟んで return <>{children}</>;
としている理由が僕には分かりませんが(理由が分かる方教えてください)、素敵なコンポーネントです!
使用例は次の通りです。
<IF condition={user.loggedIn}>
<button>Log out</button>
</IF>
パッと見て分かりやすい!いやぁ、シンプルですね~~
僕はさっそくパクらせて導入させていただきました。
IF コンポーネントに対する他の人の反応
元ネタページのコメント欄には、IF コンポーネントに対する様々な反応が寄せられています。
三項演算子を使えばいいやん
IF コンポーネント使わなくても、三項演算子で書けるやんという趣旨のコメントがありました。
<div className="main"> {
user.loggedIn ? (<button>Log out</button>) : null;
} </div>
・・・美しさのかけらもありません(個人的感想です)。
子ノードが短ければまだ見るに堪えられますけど、子ノードが複雑なものになるととても見られたものじゃありません。
それよりもですが、最後にチョロっと付く null
って部分がキモチ悪いことこのうえない!
このキモチ、実際にコードを書いてる方なら分かってくれると思いますが・・・
僕はそのキモチを少しでも和らげようと null
を前に持ってきて、
<div className="main"> {
!user.loggedIn ? null :
(<button>Log out</button>);
} </div>
というような書き方を以前はよくしましたが、それでも「前に持ってきてもnull
キモチ悪いなぁ・・・なんとからんもんかなぁ・・・」ずっと思っていました。僕は断然 IF コンポーネントを支持します!
Switch コンポーネント
Switch コンポーネントを自作したよ、こっちの方が便利だよ!という趣旨のコメントもあります。
コメントが流れてしまうともったいないくらいよいコンポーネントなので、抜粋して転載させていただきます。
const SwitchContext = React.createContext()
function ensureArray(ar) {
return Array.isArray(ar) ? ar:[ar].filter((f) => f!==undefined)
}
function noop() {}
export function Switch({ value, children }) {
const [switchContext] = useState({ cases: {} })
switchContext.value = value
return <SwitchContext.Provider value={switchContext}>{children}</SwitchContext.Provider>
}
export function Case({ when, children, execute = noop }) {
const toCheck = ensureArray(when)
const { value, cases } = useContext(SwitchContext)
let condition = toCheck.some((when) => {
if (typeof when === "function") {
return when(value)
} else {
return when === value
}
})
cases["" + when] = condition
if (condition) {
execute()
return <>{children}</>
} else {
return null
}
}
export function CaseElse({ children }) {
const { cases } = useContext(SwitchContext)
if (!Object.values(cases).some((v) => !!v)) {
return <>{children}</>
}
return null
}
使用例:
<Switch value={column.type}>
<Case when="action">
<TableCell className={classes.tight} onClick={prevent(noop)}>
<LoadedAction column={column} item={item} />
</TableCell>
</Case>
<Case when={["more", "than", "one", "reason", ()=>column.name === "including this!"]}>
{/* Some other cell */}
</Case>
{/* Other cases */}
<CaseElse>
<TableCell className={classes.tight}>
<LoadedColumn column={column} item={item} />
</TableCell>
</CaseElse>
</Switch>
余計なコードがあったり、ほんとにこのコードで大丈夫なんかなぁと思える部分がないではありませんが、工夫された素敵なコンポーネントです!
useContext()
はこの Switch コンポーネントのためにあるんじゃないかと思えるくらいにキマっています。
ただ Switch までは・・・
Switch コンポーネント素敵ですが、ここまではいらんかなぁ、というのが個人的な感想です。
例えばですが、次のようなコンポーネントを作ると、switch
文のみならず、どんな「文」も書けるようになります。
// Imme 子ノードを即時実行するコンポーネント
const Imme = ({ children }) => {
return children();
}
// 使用例
<Imme>
{() => {
// switch のみならずどんな文も書ける
switch (column.type) {
case "action": return(
<TableCell className={classes.tight} onClick={prevent(noop)}>
<LoadedAction column={column} item={item} />
</TableCell>
)
case "more": case "than": case "one": case "reason":
{/* Some other cell */}
default: return(
<TableCell className={classes.tight}>
<LoadedColumn column={column} item={item} />
</TableCell>
)
}
}}
</Imme>
特段これをキモチ悪いとも感じません(個人的感想です)。
テクニック的には Render props というやつで、中でも、children
を関数に限定してしまう手法です。
あと、実直に即時関数を使ってもそれほどヘンテコになるわけではありません。
<div className="main"> {
function immediately() {
// switch のみならずどんな文も書ける
// :
}``
} </div>
僕の即時関数の書き方については、前回の記事を参照してください。
僕からは以上です
最後までこのような拙文に付き合っていただき、ありがとうございました。