useState、useRef、useContext、useReducer:Reactフォーム要素の状態管理を徹底解説
Reactでフォーム要素の状態を兄弟/親要素に渡す正しい方法
useState と useRef
この方法は、フォーム要素の状態をローカルに保持し、useState
フックを使用して兄弟/親要素に渡します。
const [formData, setFormData] = useState({
name: '',
email: '',
});
const handleChange = (e) => {
setFormData({
...formData,
[e.target.name]: e.target.value,
});
};
// 兄弟要素
<input name="name" value={formData.name} onChange={handleChange} />
// 親要素
<div>{formData.name}</div>
この方法はシンプルで分かりやすいですが、フォーム要素が増えるとコードが冗長になりがちです。
useContext
この方法は、useContext
フックを使用して、フォーム要素の状態をコンポーネントツリー全体で共有します。
const FormContext = createContext({
formData: {},
setFormData: () => {},
});
const FormProvider = ({ children }) => {
const [formData, setFormData] = useState({
name: '',
email: '',
});
return (
<FormContext.Provider value={{ formData, setFormData }}>
{children}
</FormContext.Provider>
);
};
const MyComponent = () => {
const { formData, setFormData } = useContext(FormContext);
const handleChange = (e) => {
setFormData({
...formData,
[e.target.name]: e.target.value,
});
};
return (
<div>
<input name="name" value={formData.name} onChange={handleChange} />
<p>{formData.name}</p>
</div>
);
};
// 親要素
<FormProvider>
<MyComponent />
</FormProvider>
この方法は、コードをより簡潔に保ち、コンポーネント間の状態共有を容易にします。
useReducer
この方法は、useReducer
フックを使用して、フォーム要素の状態を管理します。
const reducer = (state, action) => {
switch (action.type) {
case 'UPDATE_NAME':
return {
...state,
name: action.payload,
};
case 'UPDATE_EMAIL':
return {
...state,
email: action.payload,
};
default:
return state;
}
};
const initialState = {
name: '',
email: '',
};
const [formData, dispatch] = useReducer(reducer, initialState);
const handleChange = (e) => {
dispatch({
type: `UPDATE_${e.target.name.toUpperCase()}`,
payload: e.target.value,
});
};
// 兄弟要素
<input name="name" value={formData.name} onChange={handleChange} />
// 親要素
<div>{formData.name}</div>
この方法は、複雑なフォーム状態を管理するのに役立ちます。
フォームライブラリ
Formik
や React-Hook-Form
などのフォームライブラリを使用すると、フォーム要素の状態管理をさらに簡潔化できます。
useState と useRef
const App = () => {
const [formData, setFormData] = useState({
name: '',
email: '',
});
const handleChange = (e) => {
setFormData({
...formData,
[e.target.name]: e.target.value,
});
};
return (
<div>
<input name="name" value={formData.name} onChange={handleChange} />
<p>{formData.name}</p>
</div>
);
};
export default App;
useContext
const FormContext = createContext({
formData: {},
setFormData: () => {},
});
const FormProvider = ({ children }) => {
const [formData, setFormData] = useState({
name: '',
email: '',
});
return (
<FormContext.Provider value={{ formData, setFormData }}>
{children}
</FormContext.Provider>
);
};
const MyComponent = () => {
const { formData, setFormData } = useContext(FormContext);
const handleChange = (e) => {
setFormData({
...formData,
[e.target.name]: e.target.value,
});
};
return (
<div>
<input name="name" value={formData.name} onChange={handleChange} />
<p>{formData.name}</p>
</div>
);
};
const App = () => {
return (
<FormProvider>
<MyComponent />
</FormProvider>
);
};
export default App;
useReducer
const reducer = (state, action) => {
switch (action.type) {
case 'UPDATE_NAME':
return {
...state,
name: action.payload,
};
case 'UPDATE_EMAIL':
return {
...state,
email: action.payload,
};
default:
return state;
}
};
const initialState = {
name: '',
email: '',
};
const App = () => {
const [formData, dispatch] = useReducer(reducer, initialState);
const handleChange = (e) => {
dispatch({
type: `UPDATE_${e.target.name.toUpperCase()}`,
payload: e.target.value,
});
};
return (
<div>
<input name="name" value={formData.name} onChange={handleChange} />
<p>{formData.name}</p>
</div>
);
};
export default App;
フォームライブラリ
import { Formik, Field, ErrorMessage } from 'formik';
const App = () => {
return (
<Formik
initialValues={{
name: '',
email: '',
}}
onSubmit={(values) => {
// フォーム送信時の処理
}}
>
<form>
<Field name="name" />
<ErrorMessage name="name" />
<Field name="email" />
<ErrorMessage name="email" />
<button type="submit">送信</button>
</form>
</Formik>
);
};
export default App;
React-Hook-Form
import { useForm } from 'react-hook-form';
const App = () => {
const { register, handleSubmit, errors } = useForm();
const onSubmit = (data) => {
// フォーム送信時の処理
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input name="name" ref={register} />
{errors.name && <p>{errors.name.message}</p>}
<input name="email" ref={register} />
{errors.email && <p>{errors.email.message}</p>}
<button type="submit">送信</button>
</form>
);
};
export default App;
これらのコードはあくまでもサンプルであり、実際の用途に合わせて変更する必要があります。
Reactでフォーム要素の状態を兄弟/親要素に渡す他の方法
ref
を使用して、フォーム要素の状態を兄弟/親要素に直接渡すことができます。
const App = () => {
const nameRef = useRef('');
const handleChange = (e) => {
nameRef.current = e.target.value;
};
return (
<div>
<input name="name" onChange={handleChange} />
<p>{nameRef.current}</p>
</div>
);
};
export default App;
この方法はシンプルですが、兄弟/親要素でフォーム要素の状態を直接操作できるため、コードが分かりにくくなる可能性があります。
const App = () => {
const [formData, setFormData] = useState({
name: '',
});
const handleChange = (e) => {
setFormData({
...formData,
name: e.target.value,
});
};
return (
<div>
<MyComponent formData={formData} />
</div>
);
};
const MyComponent = ({ formData }) => {
return (
<div>
<input name="name" value={formData.name} onChange={handleChange} />
<p>{formData.name}</p>
</div>
);
};
export default App;
この方法は、コードをより分かりやすく保ちますが、兄弟/親要素でフォーム要素の状態を変更するには、setFormData
関数を呼び出す必要があります。
カスタムフックを使用して、フォーム要素の状態管理を抽象化することができます。
const useForm = () => {
const [formData, setFormData] = useState({
name: '',
});
const handleChange = (e) => {
setFormData({
...formData,
name: e.target.value,
});
};
return {
formData,
handleChange,
};
};
const App = () => {
const { formData, handleChange } = useForm();
return (
<div>
<MyComponent formData={formData} handleChange={handleChange} />
</div>
);
};
const MyComponent = ({ formData, handleChange }) => {
return (
<div>
<input name="name" value={formData.name} onChange={handleChange} />
<p>{formData.name}</p>
</div>
);
};
export default App;
この方法は、コードをより簡潔に保ち、再利用性とテストのしやすさを向上させることができます。
状態管理ライブラリ
Redux
や MobX
などの状態管理ライブラリを使用して、フォーム要素の状態を管理することができます。
const store = createStore(
reducer,
{
name: '',
}
);
const App = () => {
return (
<Provider store={store}>
<MyComponent />
</Provider>
);
};
const MyComponent = () => {
const { name, handleChange } = useSelector((state) => state.form);
return (
<div>
<input name="name" value={name} onChange={handleChange} />
<p>{name}</p>
</div>
);
};
const mapStateToProps = (state) => {
return {
name: state.form.name,
};
};
const mapDispatchToProps = (dispatch) => {
return {
handleChange: (e) => {
dispatch({
type: 'UPDATE_NAME',
payload: e.target.value,
});
},
};
};
const MyComponentContainer = connect(
mapStateToProps,
mapDispatchToProps
)(MyComponent);
export default App;
この方法は、複雑なフォーム状態を管理するのに役立ちますが、学習曲線が steep で、コード量が増える可能性があります。
reactjs