Reactã®useActionStateããã¯ã解説ãéåæã¢ã¯ã·ã§ã³ã«ããç¶æ 管çãå¹çåããã¢ããªã±ãŒã·ã§ã³ã®ããã©ãŒãã³ã¹ãšãŠãŒã¶ãŒäœéšãåäžãããŸãã
React useActionStateã®å®è£ ïŒã¢ã¯ã·ã§ã³ããŒã¹ã®ç¶æ 管ç
è¿å¹Žã®ããŒãžã§ã³ã§å°å
¥ãããReactã®useActionStateããã¯ã¯ãéåæã¢ã¯ã·ã§ã³ã«èµ·å ããç¶æ
æŽæ°ã管çããããã®æŽç·Žãããã¢ãããŒããæäŸããŸãããã®åŒ·åãªããŒã«ã¯ãç¹ã«React Server Components (RSC) ããµãŒããŒã¢ã¯ã·ã§ã³ãæ±ãéã«ããã¥ãŒããŒã·ã§ã³ã®åŠçãUIã®æŽæ°ããšã©ãŒã¹ããŒãã®ç®¡çããã»ã¹ãå¹çåããŸãããã®ã¬ã€ãã§ã¯ãuseActionStateã®è©³çŽ°ãæ¢ããå®è£
ã®ããã®å®è·µçãªäŸãšãã¹ããã©ã¯ãã£ã¹ãæäŸããŸãã
ã¢ã¯ã·ã§ã³ããŒã¹ã®ç¶æ 管çã®å¿ èŠæ§ãçè§£ãã
åŸæ¥ã®Reactã®ç¶æ
管çã§ã¯ãã³ã³ããŒãã³ãå
ã§ããŒãã£ã³ã°ç¶æ
ãšãšã©ãŒã¹ããŒããå¥ã
ã«ç®¡çããããšããããããŸãããã¢ã¯ã·ã§ã³ïŒäŸïŒãã©ãŒã ã®éä¿¡ãããŒã¿ã®ãã§ããïŒãç¶æ
æŽæ°ãããªã¬ãŒãããšãéçºè
ã¯éåžžãè€æ°ã®useStateã³ãŒã«ãšãæœåšçã«è€éãªæ¡ä»¶ä»ãããžãã¯ã§ãããã®ç¶æ
ã管çããŸããuseActionStateã¯ãããã¯ãªãŒã³ã§çµ±åããããœãªã¥ãŒã·ã§ã³ãæäŸããŸãã
åçŽãªãã©ãŒã éä¿¡ã·ããªãªãèããŠã¿ãŸããããuseActionStateãªãã§ã¯ã次ã®ãããªãã®ãå¿
èŠã«ãªããããããŸããïŒ
- ãã©ãŒã ããŒã¿ã®ç¶æ 倿°ã
- ãã©ãŒã ãéä¿¡äžãã©ããã远跡ããããã®ç¶æ 倿°ïŒããŒãã£ã³ã°ç¶æ ïŒã
- ä»»æã®ãšã©ãŒã¡ãã»ãŒãžãä¿æããããã®ç¶æ 倿°ã
ãã®ã¢ãããŒãã¯ãåé·ãªã³ãŒããæœåšçãªäžæŽåã«ã€ãªããå¯èœæ§ããããŸããuseActionStateã¯ãããã®æžå¿µãåäžã®ããã¯ã«çµ±åããããžãã¯ãç°¡çŽ åããã³ãŒãã®å¯èªæ§ãåäžãããŸãã
useActionStateã®ç޹ä»
useActionStateããã¯ã¯2ã€ã®åŒæ°ãåãåããŸãïŒ
- ç¶æ æŽæ°ãå®è¡ããéåæé¢æ°ïŒãã¢ã¯ã·ã§ã³ãïŒãããã¯ãµãŒããŒã¢ã¯ã·ã§ã³ãŸãã¯ä»»æã®éåæé¢æ°ã«ããããšãã§ããŸãã
- åæç¶æ ã®å€ã
ãã®ããã¯ã¯2ã€ã®èŠçŽ ãå«ãé åãè¿ããŸãïŒ
- çŸåšã®ç¶æ ã®å€ã
- ã¢ã¯ã·ã§ã³ããã£ã¹ãããããããã®é¢æ°ããã®é¢æ°ã¯ãã¢ã¯ã·ã§ã³ã«é¢é£ããããŒãã£ã³ã°ç¶æ ãšãšã©ãŒã¹ããŒããèªåçã«ç®¡çããŸãã
åºæ¬çãªäŸã以äžã«ç€ºããŸãïŒ
import { useActionState } from 'react';
async function updateServer(prevState, formData) {
// éåæã®ãµãŒããŒæŽæ°ãã·ãã¥ã¬ãŒãããŸãã
await new Promise(resolve => setTimeout(resolve, 1000));
const data = Object.fromEntries(formData);
if (data.name === "error") {
return 'Failed to update server.';
}
return `Updated name to: ${data.name}`;
}
function MyComponent() {
const [state, dispatch] = useActionState(updateServer, 'Initial State');
async function handleSubmit(event) {
event.preventDefault();
const formData = new FormData(event.target);
const result = await dispatch(formData);
console.log(result);
}
return (
);
}
ãã®äŸã§ã¯ïŒ
updateServerã¯ããµãŒããŒã®æŽæ°ãã·ãã¥ã¬ãŒãããéåæã¢ã¯ã·ã§ã³ã§ããããã¯ä»¥åã®ç¶æ ãšãã©ãŒã ããŒã¿ãåãåããŸããuseActionStateã¯ç¶æ ã'Initial State'ã§åæåããçŸåšã®ç¶æ ãšdispatch颿°ãè¿ããŸããhandleSubmit颿°ã¯ããã©ãŒã ããŒã¿ãšå ±ã«dispatchãåŒã³åºããŸããuseActionStateã¯ãã¢ã¯ã·ã§ã³ã®å®è¡äžã«ããŒãã£ã³ã°ç¶æ ãšãšã©ãŒã¹ããŒããèªåçã«åŠçããŸãã
ããŒãã£ã³ã°ç¶æ ãšãšã©ãŒã¹ããŒãã®åŠç
useActionStateã®äž»èŠãªå©ç¹ã®1ã€ã¯ãããŒãã£ã³ã°ç¶æ
ãšãšã©ãŒã¹ããŒããçµã¿èŸŒã¿ã§ç®¡çã§ããããšã§ããdispatch颿°ã¯ãã¢ã¯ã·ã§ã³ã®çµæã§è§£æ±ºããããããã¹ãè¿ããŸããã¢ã¯ã·ã§ã³ããšã©ãŒãã¹ããŒããå Žåããããã¹ã¯ãã®ãšã©ãŒã§ãªãžã§ã¯ããããŸããããã䜿çšããŠãUIãé©å®æŽæ°ããããšãã§ããŸãã
åã®äŸã倿ŽããŠãããŒãã£ã³ã°ã¡ãã»ãŒãžãšãšã©ãŒã¡ãã»ãŒãžã衚瀺ããŠã¿ãŸãããïŒ
import { useActionState } from 'react';
import { useState } from 'react';
async function updateServer(prevState, formData) {
// éåæã®ãµãŒããŒæŽæ°ãã·ãã¥ã¬ãŒãããŸãã
await new Promise(resolve => setTimeout(resolve, 1000));
const data = Object.fromEntries(formData);
if (data.name === "error") {
throw new Error('Failed to update server.');
}
return `Updated name to: ${data.name}`;
}
function MyComponent() {
const [state, dispatch] = useActionState(updateServer, 'Initial State');
const [isSubmitting, setIsSubmitting] = useState(false);
const [errorMessage, setErrorMessage] = useState(null);
async function handleSubmit(event) {
event.preventDefault();
const formData = new FormData(event.target);
setIsSubmitting(true);
setErrorMessage(null);
try {
const result = await dispatch(formData);
console.log(result);
} catch (error) {
console.error("Error during submission:", error);
setErrorMessage(error.message);
} finally {
setIsSubmitting(false);
}
}
return (
);
}
äž»ãªå€æŽç¹ïŒ
- ããŒãã£ã³ã°ç¶æ
ãšãšã©ãŒã¹ããŒãã远跡ããããã«ã
isSubmittingãšerrorMessageã®ç¶æ 倿°ã远å ããŸããã handleSubmitå ã§ãdispatchãåŒã³åºãåã«isSubmittingãtrueã«èšå®ãããšã©ãŒããã£ããããŠerrorMessageãæŽæ°ããŸãã- éä¿¡äžã¯éä¿¡ãã¿ã³ãç¡å¹ã«ããããŒãã£ã³ã°ã¡ãã»ãŒãžãšãšã©ãŒã¡ãã»ãŒãžãæ¡ä»¶ä»ãã§è¡šç€ºããŸãã
React Server Components (RSC)ã§ã®ãµãŒããŒã¢ã¯ã·ã§ã³ãšuseActionState
useActionStateã¯ãReact Server Components (RSC) ããµãŒããŒã¢ã¯ã·ã§ã³ãšå
±ã«äœ¿çšãããšãã®ç䟡ãçºæ®ããŸãããµãŒããŒã¢ã¯ã·ã§ã³ã¯ãµãŒããŒäžã§å®è¡ãããããŒã¿ãœãŒã¹ãçŽæ¥å€æŽã§ãã颿°ã§ããããã«ãããAPIãšã³ããã€ã³ããèšè¿°ããããšãªãããµãŒããŒãµã€ãã®æäœãå®è¡ã§ããŸãã
泚ïŒãã®äŸã«ã¯ãServer ComponentsãšServer Actionsçšã«æ§æãããReactç°å¢ãå¿ èŠã§ãã
// app/actions.js (ãµãŒããŒã¢ã¯ã·ã§ã³)
'use server';
import { cookies } from 'next/headers'; //äŸïŒNext.jsã®å Žå
export async function updateName(prevState, formData) {
const name = formData.get('name');
if (!name) {
return 'Please enter a name.';
}
try {
// ããŒã¿ããŒã¹ã®æŽæ°ãã·ãã¥ã¬ãŒãããŸãã
await new Promise(resolve => setTimeout(resolve, 1000));
cookies().set('userName', name);
return `Updated name to: ${name}`; //æåïŒ
} catch (error) {
console.error("Database update failed:", error);
return 'Failed to update name.'; // éèŠïŒErrorãã¹ããŒããã®ã§ã¯ãªããã¡ãã»ãŒãžãè¿ããŸã
}
}
// app/page.jsx (React Server Component)
'use client';
import { useActionState } from 'react';
import { updateName } from './actions';
function MyComponent() {
const [state, dispatch] = useActionState(updateName, 'Initial State');
async function handleSubmit(event) {
event.preventDefault();
const formData = new FormData(event.target);
const result = await dispatch(formData);
console.log(result);
}
return (
);
}
export default MyComponent;
ãã®äŸã§ã¯ïŒ
updateNameã¯app/actions.jsã§å®çŸ©ããããµãŒããŒã¢ã¯ã·ã§ã³ã§ããããã¯ä»¥åã®ç¶æ ãšãã©ãŒã ããŒã¿ãåãåããããŒã¿ããŒã¹ãæŽæ°ãïŒã·ãã¥ã¬ãŒãïŒãæåãŸãã¯ãšã©ãŒã¡ãã»ãŒãžãè¿ããŸããéèŠãªã®ã¯ãã¢ã¯ã·ã§ã³ããšã©ãŒãã¹ããŒããã®ã§ã¯ãªãã¡ãã»ãŒãžãè¿ãç¹ã§ãããµãŒããŒã¢ã¯ã·ã§ã³ã§ã¯ãæçãªã¡ãã»ãŒãžãè¿ãããšãæšå¥šãããŸãã- ã³ã³ããŒãã³ãã¯ã
useActionStateããã¯ã䜿çšããããã«ã¯ã©ã€ã¢ã³ãã³ã³ããŒãã³ãïŒ'use client'ïŒãšããŠããŒã¯ãããŠããŸãã handleSubmit颿°ã¯ãã©ãŒã ããŒã¿ãšå ±ã«dispatchãåŒã³åºããŸããuseActionStateã¯ããµãŒããŒã¢ã¯ã·ã§ã³ã®çµæã«åºã¥ããŠç¶æ æŽæ°ãèªåçã«ç®¡çããŸãã
ãµãŒããŒã¢ã¯ã·ã§ã³ã«é¢ããéèŠãªèæ ®äºé
- ãµãŒããŒã¢ã¯ã·ã§ã³ã§ã®ãšã©ãŒãã³ããªã³ã°ïŒãšã©ãŒãã¹ããŒãã代ããã«ããµãŒããŒã¢ã¯ã·ã§ã³ããæå³ã®ãããšã©ãŒã¡ãã»ãŒãžãè¿ããŸãã
useActionStateã¯ãã®ã¡ãã»ãŒãžãæ°ããç¶æ ãšããŠæ±ããŸããããã«ãããã¯ã©ã€ã¢ã³ãåŽã§åªé ãªãšã©ãŒãã³ããªã³ã°ãå¯èœã«ãªããŸãã - ãªããã£ãã¹ãã£ãã¯ã¢ããããŒãïŒãµãŒããŒã¢ã¯ã·ã§ã³ã¯ãäœæããã©ãŒãã³ã¹ãåäžãããããã«ãªããã£ãã¹ãã£ãã¯ã¢ããããŒããšå ±ã«äœ¿çšã§ããŸããUIãå³åº§ã«æŽæ°ããã¢ã¯ã·ã§ã³ã倱æããå Žåã«å ã«æ»ãããšãã§ããŸãã
- åæ€èšŒïŒãã¥ãŒããŒã·ã§ã³ãæåããåŸããã£ãã·ã¥ãããããŒã¿ãåæ€èšŒããŠãUIãææ°ã®ç¶æ ãåæ ããããã«ããããšãæ€èšããŠãã ããã
useActionStateã®é«åºŠãªãã¯ããã¯
1. è€éãªç¶æ æŽæ°ã®ããã®Reducerã®äœ¿çš
ããè€éãªç¶æ
ããžãã¯ã®å ŽåãuseActionStateãReducer颿°ãšçµã¿åãããããšãã§ããŸããããã«ãããäºæž¬å¯èœã§ä¿å®ããããæ¹æ³ã§ç¶æ
æŽæ°ã管çã§ããŸãã
import { useActionState } from 'react';
import { useReducer } from 'react';
const initialState = {
count: 0,
message: 'Initial State',
};
function reducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + 1 };
case 'DECREMENT':
return { ...state, count: state.count - 1 };
case 'SET_MESSAGE':
return { ...state, message: action.payload };
default:
return state;
}
}
async function updateState(state, action) {
// éåææäœãã·ãã¥ã¬ãŒãããŸãã
await new Promise(resolve => setTimeout(resolve, 500));
switch (action.type) {
case 'INCREMENT':
return reducer(state, action);
case 'DECREMENT':
return reducer(state, action);
case 'SET_MESSAGE':
return reducer(state, action);
default:
return state;
}
}
function MyComponent() {
const [state, dispatch] = useActionState(updateState, initialState);
return (
Count: {state.count}
Message: {state.message}
);
}
2. useActionStateã«ãããªããã£ãã¹ãã£ãã¯ã¢ããããŒã
ãªããã£ãã¹ãã£ãã¯ã¢ããããŒãã¯ãã¢ã¯ã·ã§ã³ãæåãããã®ããã«UIãå³åº§ã«æŽæ°ããã¢ã¯ã·ã§ã³ã倱æããå Žåã«æŽæ°ãå ã«æ»ãããšã§ããŠãŒã¶ãŒãšã¯ã¹ããªãšã³ã¹ãåäžãããŸããããã«ãããã¢ããªã±ãŒã·ã§ã³ãããã¬ã¹ãã³ã·ãã«æããããããã«ãªããŸãã
import { useActionState } from 'react';
import { useState } from 'react';
async function updateServer(prevState, formData) {
// éåæã®ãµãŒããŒæŽæ°ãã·ãã¥ã¬ãŒãããŸãã
await new Promise(resolve => setTimeout(resolve, 1000));
const data = Object.fromEntries(formData);
if (data.name === "error") {
throw new Error('Failed to update server.');
}
return `Updated name to: ${data.name}`;
}
function MyComponent() {
const [name, setName] = useState('Initial Name');
const [state, dispatch] = useActionState(async (prevName, newName) => {
try {
const result = await updateServer(prevName, {
name: newName,
});
return newName; // æåæã«æŽæ°
} catch (error) {
// ãšã©ãŒæã«å
ã«æ»ã
console.error("Update failed:", error);
setName(prevName);
return prevName;
}
}, name);
async function handleSubmit(event) {
event.preventDefault();
const formData = new FormData(event.target);
const newName = formData.get('name');
setName(newName); // UIããªããã£ãã¹ãã£ãã¯ã«æŽæ°
await dispatch(newName);
}
return (
);
}
3. ã¢ã¯ã·ã§ã³ã®ãããŠã³ã¹
äžéšã®ã·ããªãªã§ã¯ãã¢ã¯ã·ã§ã³ãé »ç¹ã«ãã£ã¹ããããããã®ãé²ãããã«ãã¢ã¯ã·ã§ã³ããããŠã³ã¹ãããå ŽåããããŸããããã¯ããŠãŒã¶ãŒãäžå®æéã¿ã€ãã³ã°ã忢ããåŸã«ã®ã¿ã¢ã¯ã·ã§ã³ãããªã¬ãŒãããæ€çŽ¢å ¥åã®ãããªã·ããªãªã§åœ¹ç«ã¡ãŸãã
import { useActionState } from 'react';
import { useState, useEffect } from 'react';
async function searchItems(prevState, query) {
// éåææ€çŽ¢ãã·ãã¥ã¬ãŒãããŸãã
await new Promise(resolve => setTimeout(resolve, 500));
return `Search results for: ${query}`;
}
function MyComponent() {
const [query, setQuery] = useState('');
const [state, dispatch] = useActionState(searchItems, 'Initial State');
useEffect(() => {
const timeoutId = setTimeout(() => {
if (query) {
dispatch(query);
}
}, 300); // 300msã®ãããŠã³ã¹
return () => clearTimeout(timeoutId);
}, [query, dispatch]);
return (
setQuery(e.target.value)}
/>
State: {state}
);
}
useActionStateã®ãã¹ããã©ã¯ãã£ã¹
- ã¢ã¯ã·ã§ã³ãçŽç²ã«ä¿ã€ïŒã¢ã¯ã·ã§ã³ãçŽç²é¢æ°ïŒãŸãã¯ããã«è¿ããã®ïŒã§ããããšã確èªããŠãã ãããç¶æ ã®æŽæ°ä»¥å€ã®å¯äœçšãæã€ã¹ãã§ã¯ãããŸããã
- ãšã©ãŒãé©åã«åŠçããïŒã¢ã¯ã·ã§ã³å ã®ãšã©ãŒãåžžã«åŠçãããŠãŒã¶ãŒã«æçãªãšã©ãŒã¡ãã»ãŒãžãæäŸããŠãã ããããµãŒããŒã¢ã¯ã·ã§ã³ã§åè¿°ããããã«ããšã©ãŒãã¹ããŒããã®ã§ã¯ãªãããµãŒããŒã¢ã¯ã·ã§ã³ãããšã©ãŒã¡ãã»ãŒãžæååãè¿ãããšãåªå ããŠãã ããã
- ããã©ãŒãã³ã¹ãæé©åããïŒç¹ã«å€§ããªããŒã¿ã»ãããæ±ãå Žåãã¢ã¯ã·ã§ã³ã®ããã©ãŒãã³ã¹ãžã®åœ±é¿ã«æ³šæããŠãã ãããäžèŠãªåã¬ã³ããªã³ã°ãé¿ããããã«ãã¡ã¢åæè¡ã®äœ¿çšãæ€èšããŠãã ããã
- ã¢ã¯ã»ã·ããªãã£ãèæ ®ããïŒé害ãæã€ãŠãŒã¶ãŒãå«ããã¹ãŠã®ãŠãŒã¶ãŒãã¢ããªã±ãŒã·ã§ã³ã«ã¢ã¯ã»ã¹ã§ããããã«ããŠãã ãããé©åãªARIA屿§ãšããŒããŒãããã²ãŒã·ã§ã³ãæäŸããŠãã ããã
- 培åºçãªãã¹ãïŒåäœãã¹ããšçµ±åãã¹ããäœæããã¢ã¯ã·ã§ã³ãšç¶æ ã®æŽæ°ãæ£ããæ©èœããŠããããšã確èªããŠãã ããã
- åœéåïŒi18nïŒïŒã°ããŒãã«ãªã¢ããªã±ãŒã·ã§ã³ã®å Žåãè€æ°ã®èšèªãšæåããµããŒãããããã«i18nãå®è£ ããŠãã ããã
- ããŒã«ãªãŒãŒã·ã§ã³ïŒl10nïŒïŒããŒã«ã©ã€ãºãããã³ã³ãã³ããæ¥ä»åœ¢åŒãé貚èšå·ãæäŸããããšã§ãã¢ããªã±ãŒã·ã§ã³ãç¹å®ã®ãã±ãŒã«ã«åãããŠèª¿æŽããŠãã ããã
useActionStateãšä»ã®ç¶æ 管çãœãªã¥ãŒã·ã§ã³ãšã®æ¯èŒ
useActionStateã¯ã¢ã¯ã·ã§ã³ããŒã¹ã®ç¶æ
æŽæ°ã管çãã䟿å©ãªæ¹æ³ãæäŸããŸããããã¹ãŠã®ç¶æ
管çãœãªã¥ãŒã·ã§ã³ã®ä»£æ¿ãšãªãããã§ã¯ãããŸãããè€æ°ã®ã³ã³ããŒãã³ãéã§å
±æããå¿
èŠã®ããã°ããŒãã«ãªç¶æ
ãæã€è€éãªã¢ããªã±ãŒã·ã§ã³ã«ã¯ãReduxãZustandãJotaiã®ãããªã©ã€ãã©ãªãããé©ããŠããå ŽåããããŸãã
useActionStateã䜿çšããå ŽåïŒ
- åçŽããäžçšåºŠã®è€éãã®ç¶æ æŽæ°ã
- éåæã¢ã¯ã·ã§ã³ãšå¯æ¥ã«çµåããç¶æ æŽæ°ã
- React Server Componentsããã³Server Actionsãšã®çµ±åã
ä»ã®ãœãªã¥ãŒã·ã§ã³ãæ€èšããå ŽåïŒ
- è€éãªã°ããŒãã«ç¶æ 管çã
- 倿°ã®ã³ã³ããŒãã³ãéã§å ±æããå¿ èŠãããç¶æ ã
- ã¿ã€ã ãã©ãã«ãããã°ãããã«ãŠã§ã¢ãªã©ã®é«åºŠãªæ©èœã
çµè«
Reactã®useActionStateããã¯ã¯ãéåæã¢ã¯ã·ã§ã³ã«ãã£ãŠããªã¬ãŒãããç¶æ
æŽæ°ã管çããããã®åŒ·åã§ãšã¬ã¬ã³ããªæ¹æ³ãæäŸããŸããããŒãã£ã³ã°ç¶æ
ãšãšã©ãŒã¹ããŒããçµ±åããããšã§ãç¹ã«React Server ComponentsããµãŒããŒã¢ã¯ã·ã§ã³ãæ±ãéã«ãã³ãŒããç°¡çŽ åããå¯èªæ§ãåäžãããŸãããã®é·æãšéçãçè§£ããããšã§ãã¢ããªã±ãŒã·ã§ã³ã«é©ããç¶æ
管çã¢ãããŒããéžæã§ããããä¿å®æ§ãé«ãå¹ççãªã³ãŒãã«ã€ãªãããŸãã
ãã®ã¬ã€ãã§æŠèª¬ãããã¹ããã©ã¯ãã£ã¹ã«åŸãããšã§ãuseActionStateã广çã«æŽ»çšããŠãã¢ããªã±ãŒã·ã§ã³ã®ãŠãŒã¶ãŒãšã¯ã¹ããªãšã³ã¹ãšéçºã¯ãŒã¯ãããŒãåäžãããããšãã§ããŸããã¢ããªã±ãŒã·ã§ã³ã®è€éããèæ
®ããããŒãºã«æãåã£ãç¶æ
管çãœãªã¥ãŒã·ã§ã³ãéžæããããšãå¿ããªãã§ãã ãããåçŽãªãã©ãŒã éä¿¡ããè€éãªããŒã¿ãã¥ãŒããŒã·ã§ã³ãŸã§ãuseActionStateã¯Reactéçºã®æŠåšåº«ã§äŸ¡å€ããããŒã«ãšãªãåŸãŸãã