{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreib67uattnmpovnmcejyijxrzlinyn7cb2cdjyylcoj3cywgxn2hle",
"uri": "at://did:plc:25rdn5elo5izoxrmtis34zuk/app.bsky.feed.post/3moraklznhoe2"
},
"coverImage": {
"$type": "blob",
"ref": {
"$link": "bafkreigbyan6h6gz2ngior7otw4ezabsan5qrbem64bt2hzjajac6nvhku"
},
"mimeType": "image/webp",
"size": 108386
},
"path": "/kkr0423/reactjs-usestate-antipatterns-58i9",
"publishedAt": "2026-06-21T01:06:41.000Z",
"site": "https://dev.to",
"tags": [
"webdev",
"programming",
"react",
"frontend"
],
"textContent": "**1. Consider grouping related conditions together**\n\n * Before\n\n\n\n\n const [x, setX] = useState(0);\n const [y, setY] = useState(0);\n\n const handlePointerMove = (e) => {\n setX(e.clientX);\n setY(e.clientY);\n };\n return (\n <div\n onPointerMove={handlePointerMove}\n style={{\n width: \"100vw\",\n height: \"100vh\",\n }}\n />\n );\n\n\n * After\n\n\n\n\n const [position, setPosition] = useState({ x: 0, y: 0 });\n\n const handlePointerMove = (e) => {\n setPosition({\n x: e.clientX,\n y: e.clientY,\n });\n };\n return (...\n );\n\n\n\n * Before\n\n\n\n\n const [userName, setUserName] = useState(\"\");\n const [userAge, setUserAge] = useState(0);\n\n\n * After\n\n\n\n\n const [userInfo, setUserInfo] = useState({name:\"\",age:0});\n\n\n**2. Avoid declaring conflicting states**\n\n * Before\n\n\n\n\n export default function Form() {\n const [text, setText] = useState('');\n const [isSubmitting, setSubmitting] = useState(false);\n const [isSubmit, setIsSubmit] = useState(false);\n\n async function handleSubmit(e) {\n e.preventDefault();\n isSubmitting(true);\n await sendMessage(text);\n isSubmitting(false);\n setIsSubmit(true);\n }\n\n if (isSubmit) {\n return <h1>I appreciate</h1>\n }\n\n function sendMessage(text) {\n return new Promise(resolve => {\n setTimeout(resolve, 2000);\n });\n }\n\n return (\n <form onSubmit={handleSubmit}>\n <textarea\n disabled={isSending}\n value={text}\n onChange={e => setText(e.target.value)}\n />\n <br />\n <button\n disabled={isSending}\n type=\"submit\"\n >\n Submit\n </button>\n {isSubmitting && <p>Submitting...</p>}\n </form>\n );\n }\n\n\n * After\n\n\n\n\n export default function Form() {\n const [text, setText] = useState('');\n const [status, setStatus] = useState('TYPING');\n\n async function handleSubmit(e) {\n e.preventDefault();\n setStatus('SUBMITTING');\n await sendMessage(text);\n setStatus('SUBMITTED');\n }\n\n const isSending = status === 'SUBMITTING';\n const isSent = status === 'SUBMITTED';\n\n return (...\n );\n }\n\n\n`isSubmit` and `isSubmitting` are conflicting and make it difficult to handle these states as it gets complex.\nYou have to group them into a single state.\n\n**3. Avoid redundant use**\n\n\n\n export default function Form() {\n const [firstName, setFirstName] = useState('');\n const [lastName, setLastName] = useState('');\n const [fullName, setFullName] = useState('');\n\n function handleFirstNameChange(e) {\n setFirstName(e.target.value);\n setFullName(e.target.value + ' ' + lastName);\n }\n\n function handleLastNameChange(e) {\n setLastName(e.target.value);\n setFullName(firstName + ' ' + e.target.value);\n }\n\n return (\n <>\n <label>\n First name:\n <input\n name=\"firstName\"\n value={firstName}\n onChange={handleFirstNameChange}\n />\n </label>\n <label>\n Last name:\n <input\n name=\"lastName\"\n value={lastName}\n onChange={handleLastNameChange}\n />\n </label>\n <p>\n FullName: <span>{fullName}</span>\n </p>\n </>\n );\n }\n\n\n\nThe `fullName` can be calculated when rendering by `firstName` + `lastName`.\n\n\n\n const [userName, setUserName] = useState({ firstName: \"\", lastName: \"\" });\n const fullName = userName.firstName + \" \" + userName.lastName;\n\n const handleUserNameChange = (e) => {\n setUserName({ ...userName, [e.target.name]: e.target.value });\n };\n\n\n\nAnd props should not be held in the state.\n\n\n\n function Text({ children, color }) {\n const [textColor] = useState(color);\n\n return <h1 style={{ color: textColor }}>{children}</h1>;\n }\n\n export default function Example() {\n const [color, setColor] = useState(\"red\");\n\n return (\n <div>\n <p>\n Select Color\n <select value={color} onChange={(e) => setColor(e.target.value)}>\n <option value=\"red\">Red</option>\n <option value=\"blue\">Blue</option>\n <option value=\"green\">Green</option>\n </select>\n </p>\n <Text color={color}>Color will be changed</Text>\n </div>\n );\n }\n\n\n\nThe color seems to change at a glance, but doesn't change.\nBecause `useState` is initialized when first rendering.\n\n * Fixed\n\n\n\n\n function Text({ children, color }) {\n const textColor = color;\n\n return <h1 style={{ color: textColor }}>{children}</h1>;\n }\n\n\n\n**4. Avoid declaring the same state multiple times**\n\n\n\n const initialItems = [\n { id: 1, title: \"taskA\" },\n { id: 2, title: \"taskB\" },\n { id: 3, title: \"taskC\" },\n ];\n\n export default function TaskList() {\n const [tasks, setTasks] = useState(initialItems);\n const [selectedTask, setSelectedTask] = useState(tasks[0]);\n\n function handleTaskChange(id, e) {\n setTasks(tasks.map((task) => (task.id === id ? { ...task, title: e.target.value } : task)));\n setSelectedTask((task) => (task.id === id ? { ...task, title: e.target.value } : task));\n }\n\n return (\n <>\n <h2>Task List</h2>\n <ul>\n {tasks.map((task, index) => (\n <li key={task.id}>\n <input\n value={task.title}\n onChange={(e) => {\n handleTaskChange(task.id, e);\n }}\n />\n <button\n onClick={() => {\n setSelectedTask(task);\n }}\n >\n Select\n </button>\n </li>\n ))}\n </ul>\n <p>Today's task {selectedTask.title}</p>\n </>\n );\n }\n\n\n\nThe `task` and the `selectedTask` handle the same data.\nWhen you edit the task, you have to update `selectedTask`.\n\nSo, fix the codebase to calculate the `selectedTask` by id\n\n\n\n function TaskList() {\n const [tasks, setTasks] = useState(initialItems);\n const [selectedTaskId, setSelectedTaskId] = useState(0);\n\n function handleTaskChange(id, e) {\n setTasks(tasks.map((task) => (task.id === id ? { ...task, title: e.target.value } : task)));\n }\n\n const selectedTask = tasks.find((task) => task.id === selectedTaskId);\n\n return (\n <>\n <h2>Task List</h2>\n <ul>\n {tasks.map((task) => (\n <li key={task.id}>\n <input\n value={task.title}\n onChange={(e) => {\n handleTaskChange(task.id, e);\n }}\n />\n <button\n onClick={() => {\n setSelectedTaskId(task.id);\n }}\n >\n Select\n </button>\n </li>\n ))}\n </ul>\n <p>Today's Task {selectedTask.title}</p>\n </>\n );\n }\n\n",
"title": "React.js ~useState Antipatterns~"
}