Inputフィールドの作成
コンポーネントを一通り学んだ後に簡単なInputフィールドを作って遊んでみました。
しかし、意外と簡単にはいかず、良い復習になったので備忘録を残しておきます。
簡単な書き方
こんな感じで書いてみました。
ブラウザから見るととりあえず動いています。
import React, { useState, useCallback, useMemo } from 'react';
import './App.css';
const App=()=> {
return (
<div className="App">
<header className="App-header">
<input type="text"/>
</header>
</div>
);
}
export default App;
この場合、コンポーネントの状態 (state) を全く記述しておらず、入力の状態をどう保持するのかって感じです。
*このようなコンポーネントを「ステートレスコンポーネント (stateless component)」と言うそうです。
また、値の状態がビューと切り離されておらず、アプリケーションの設計としては良いとは言えないです。
stateの管理
Inputの値(value)をstate
で管理します。
const App=()=> {
const [message, setMessage] = useState('');
return (
<div className="App">
<header className="App-header">
<input type="text" value={message}/>
</header>
</div>
);
}
const [message, setMessage] = useState('')
でstate
を保持します。
-
message
.. state変数 -
setMessage
.. state変数の状態を変更するための関数
クラスコンポーネントはthis.state
と言う書き方で内部の値を保持することができましたが、関数コンポーネントでは同様の書き方はできません。そこで、HookのuseState
を使ってあげて変数の状態を保持することを可能とします。
参考)useState を呼ぶと何が起きるの?
これにより『state 変数』が宣言されます。useState は、クラスにおいて this.state が提供するのと全く同じ機能を実現するための新しい方法です。通常、関数が終了すると変数は『消えて』しまいますが、state 変数は React によって保持されます。
これで再度ブラウザから動きを確認します。。。
何も入力できないことが分かります。
stateの変更
ここで公式ドキュメントのuseState
を再度確認します。
useState
は「state変数とstate変数の状態を変更するための関数」を返します。
2つ目の戻り値と言うのが「state変数の状態を変更するための関数」であり、言い換えると「state変数の状態を変更したい場合はこの関数を呼ぶ」と言うことです。これをしっかりと理解していないと上のようなことが起きてしまいます。
カウントダウンのような機能の場合、関数コンポーネント内に以下のように書きます。
const [count, setCount] = useState(0);
<button onClick={() => setCount(count + 1)}>
messageの状態を変更するための関数setMessage
を定義を呼び出すための関数(ハンドラ)を定義し、inputフィールドのonChanged
属性に指定します。
const App=()=> {
const [message, setMessage] = useState('');
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setMessage(e.target.value);
};
return (
<div className="App">
<header className="App-header">
<input type="text" value={message} onChange={handleChange} />
</header>
</div>
);
}
これでやりたいことは実現できます。
このようなにコンポーネントの内容をstateの状態で保持するものを制御コンポーネントと言うみたいです。
コンポーネントの分割
おまけにコンポーネントの分割も試してみます。
今回は簡単のため、App
コンポーネントにinputフィールドを記述しました。
inputフィールドが1つの場合は良いかもしれないですが、複数の場合はコンポーネントの分割を検討するのが良いかもしれません。
こんな感じでInputText
のコンポーネントを作ってみました。
InputText
が受け取る引数をProps
で定義しておきます。
import React from 'react';
import styled from 'styled-components';
const Input = styled.input`
height: 60px;
width: 450px;
border: 1px solid #BEBEBE;
box-sizing: border-box;
border-radius: 6px;
padding-left: 16px;
font-size: 24px;
color: #222222;
::placeholder {
color: #C1C1C1;
}
`;
type Props = {
value: string,
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void,
placeholder: string,
};
const InputText = (props: Props) => {
const {
value,
onChange,
placeholder,
} = props;
return (
<Input
type=""
value={value}
placeholder={placeholder}
onChange={onChange}
/>
);
};
export default InputText;
これをApp
コンポーネントで呼び出してあげます。
import React, { useState, useCallback, useMemo } from 'react';
import logo from './logo.svg';
import './App.css';
import InputText from './components/elements/inputText';
const App=()=> {
const [message, setMessage] = useState('');
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setMessage(e.target.value);
};
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<InputText
value={message}
placeholder="ID"
onChange={handleChange}
/>
</header>
</div>
);
}
export default App;
こんな感じで簡単にコンポーネントの分割が実現できました。