投稿目的
- よりよい方法をご教授いただきたい
- 「ふわっとした理解」を「ぷにっとした理解」くらいに固める
本題
きっかけ
個人開発内(TypeScript×React)で下記のような実装をしたいと思いました。
・チェックボックスが
checked
の間、<input type='text' />
を表示する
・チェックボックスがchecked
ではない間、<input type='text' />
を表示しない
何もせず「&&」でコンポーネントを非表示にすると...
&&
演算子は、「左辺がtruthyなら右辺を返却する」「左辺がfalsyなら左辺を返却する」演算子です。
・truthy
→ falsy以外の値
・falsy
-string
型の「""
(empty-string)」
-number
型の「0
」
-boolean
型の「false
」
-null
-undefined
-NaN
(Not a Number)
(参考)
これを用いると、下記のような実装で表示制御が可能となります。(styled-components
によるスタイル定義については省略)
Stack Blitz
import { FC, useState } from 'react';
import styled from 'styled-components';
export const DefaultPage: FC = () => {
const [isVisible, setIsVisible] = useState<boolean>(false);
return (
<>
<SContainer>
<SCheckboxArea>
<input
type="checkbox"
id="mr-checkbox"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setIsVisible(e.currentTarget.checked);
}}
/>
<label htmlFor="mr-checkbox">表示を制御する</label>
</SCheckboxArea>
{/** 「&&」で表示制御 */}
{isVisible && <input type="text" />}
</SContainer>
</>
);
};
isVisible
がtrue
の間は<input type="text" />
が表示されていることが分かります。
しかし、何かを入力(①)してからチェックを外し、再度チェックをつけると、①の入力内容が消えていることが分かります。
これは、isVisible
がfalse
になっている間、<input type="text" />
がunmountされている (HTMLから消えている) ことが原因です。
・isVisible
がtrue
の間のHTML
<div>
<div>
<input type="checkbox" checked />
<label>表示を制御する</label>
</div>
<!-- 表示されている -->
<input type="text" />
</div>
・isVisible
がfalse
の間のHTML
<div>
<div>
<input type="checkbox" />
<label>表示を制御する</label>
</div>
<!-- <input type='text' />が消えている -->
</div>
isVisible
がfalse
からtrue
に切り替わるたびに、新たな<input type='text' />
が生成・描画されているイメージです。
【解決策】王道 state
を使う
<input type='text' />
にonChange
ついてねーじゃん と思われた方も多いと思います。
unmount後も入力した値がstate
に保持されるため、再度<input type='text' />
がmountされた (HTML上に描画された) 際にvalue
プロパティに値を設定することができます。
import { FC, useState } from 'react';
import styled from 'styled-components';
export const StatePage: FC = () => {
const [isVisible, setIsVisible] = useState<boolean>(false);
// 入力値をstateで保持
const [inputValue, setInputValue] = useState<string>("");
return (
<>
<SContainer>
<SCheckboxArea>
<input
type="checkbox"
id="mr-checkbox"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setIsVisible(e.currentTarget.checked);
}}
/>
<label htmlFor="mr-checkbox">表示を制御する</label>
</SCheckboxArea>
{/** 「&&」で表示制御 */}
{isVisible && (
<input
type="text"
value={inputValue}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setInputValue(e.currentTarget.value);
}}
/>)
}
</SContainer>
</>
);
};
チェックを一度外した後にもう一度つけてみると、入力内容が保持されていることが分かります。
【解決策】邪道(?) CSSでどうにかする
CSSでdisplay: none;
やvisibitity: hidden; height: 0;
などを表示制御したい範囲に設定すると、当該範囲が非表示になってくれます。
しかも、unmountされたわけではないため、HTML上からは消えず、state
でvalue
プロパティを保持しなくてもvalue
プロパティの値が消えません。
(参考)
私の場合、<HiddenArea />
というcomponentを作成し、その子要素(children
)に表示制御したい要素を設定しました。
import { FC, ReactNode, memo } from "react";
import styled from "styled-components";
type HiddenAreaProps = {
/**
* 子要素
* @type {ReactNode}
*/
children: ReactNode;
/**
* 見えているか
* @type {boolean}
*/
isVisible: boolean;
};
export const HiddenArea: FC<HiddenAreaProps> = (props: HiddenAreaProps) => {
const { children, isVisible } = props;
return (
{/** isVisibleがfalseの場合、display:none; で非表示 */}
<SContainer style={isVisible ? {} : { display: "none" }}>
{children}
</SContainer>
);
};
import { FC, useState } from 'react';
import styled from 'styled-components';
import { HiddenArea } from '../../aboutUnmount/HiddenArea';
export const CssPage: FC = () => {
const [isVisible, setIsVisible] = useState<boolean>(false);
return (
<>
<SContainer>
<SCheckboxArea>
<input
type="checkbox"
id="mr-checkbox"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setIsVisible(e.currentTarget.checked);
}}
/>
<label htmlFor="mr-checkbox">表示を制御する</label>
</SCheckboxArea>
{/** CSSで表示制御 */}
<HiddenArea children={<input type="text" />} isVisible={isVisible} />
</SContainer>
</>
);
};
こちらも、state
を用いた場合と同じ結果になります。
まとめ
<input />
のvalue
操作×表示制御 にはstate
を用いるのが一般的ですが、それ以外にも方法はあるんだよ~というご紹介でした。
state
を用いると入力値をリアルタイムで把握できるため、「入力値が5桁未満の場合はボタンを非活性にする」「全角スペースが入力された場合、半角スペースに変換して入力値に反映する」といったことが可能になります。
ただ、state
として管理したい変数が多くなってくるとコードが煩雑になったり、システムが重くなってしまうことがあります...
今回紹介した方法は、入力値のリアルタイムな把握はできませんが、「ボタンがクリックされた時の入力値を取得する」などといった実装は可能です。
【イメージ】
①ボタンをクリックし、入力値を取得
②入力値のバリデーションや整形を実行
③APIのパラメータとしてセット
余談
この方法を思い出したのは、現場での経験によるものです。
ある日先輩に「ハンバーガメニューの表示制御ってどうやってたんだっけ? state
によって出し分けてたっけ?」と質問をいただき確認すると、
①アイコンをクリックすると、
isOpen
(state
で管理されている変数) がtrue
となる
②isOpen
によって、ハンバーガメニューに適用されるclassName
が変化する
③isOpen
がtrue
の時は、visibility: visible;
が、false
の時はvisibility: collapse;
が適用されるclassName
に変化し、表示制御が行われる
という実装になっていました。
雑談って大事なんだな~と思いました、雑談を大事にします。(小並感)