1. Consider grouping related conditions together
- Before
const [x, setX] = useState(0);
const [y, setY] = useState(0);
const handlePointerMove = (e) => {
setX(e.clientX);
setY(e.clientY);
};
return (
<div
onPointerMove={handlePointerMove}
style={{
width: "100vw",
height: "100vh",
}}
/>
);
Enter fullscreen mode Exit fullscreen mode
- After
const [position, setPosition] = useState({ x: 0, y: 0 });
const handlePointerMove = (e) => {
setPosition({
x: e.clientX,
y: e.clientY,
});
};
return (...
);
Enter fullscreen mode Exit fullscreen mode
- Before
const [userName, setUserName] = useState("");
const [userAge, setUserAge] = useState(0);
Enter fullscreen mode Exit fullscreen mode
- After
const [userInfo, setUserInfo] = useState({name:"",age:0});
Enter fullscreen mode Exit fullscreen mode
2. Avoid declaring conflicting states
- Before
export default function Form() {
const [text, setText] = useState('');
const [isSubmitting, setSubmitting] = useState(false);
const [isSubmit, setIsSubmit] = useState(false);
async function handleSubmit(e) {
e.preventDefault();
isSubmitting(true);
await sendMessage(text);
isSubmitting(false);
setIsSubmit(true);
}
if (isSubmit) {
return <h1>I appreciate</h1>
}
function sendMessage(text) {
return new Promise(resolve => {
setTimeout(resolve, 2000);
});
}
return (
<form onSubmit={handleSubmit}>
<textarea
disabled={isSending}
value={text}
onChange={e => setText(e.target.value)}
/>
<br />
<button
disabled={isSending}
type="submit"
>
Submit
</button>
{isSubmitting && <p>Submitting...</p>}
</form>
);
}
Enter fullscreen mode Exit fullscreen mode
- After
export default function Form() {
const [text, setText] = useState('');
const [status, setStatus] = useState('TYPING');
async function handleSubmit(e) {
e.preventDefault();
setStatus('SUBMITTING');
await sendMessage(text);
setStatus('SUBMITTED');
}
const isSending = status === 'SUBMITTING';
const isSent = status === 'SUBMITTED';
return (...
);
}
Enter fullscreen mode Exit fullscreen mode
isSubmit and isSubmitting are conflicting and make it difficult to handle these states as it gets complex.
You have to group them into a single state.
3. Avoid redundant use
export default function Form() {
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [fullName, setFullName] = useState('');
function handleFirstNameChange(e) {
setFirstName(e.target.value);
setFullName(e.target.value + ' ' + lastName);
}
function handleLastNameChange(e) {
setLastName(e.target.value);
setFullName(firstName + ' ' + e.target.value);
}
return (
<>
<label>
First name:
<input
name="firstName"
value={firstName}
onChange={handleFirstNameChange}
/>
</label>
<label>
Last name:
<input
name="lastName"
value={lastName}
onChange={handleLastNameChange}
/>
</label>
<p>
FullName: <span>{fullName}</span>
</p>
</>
);
}
Enter fullscreen mode Exit fullscreen mode
The fullName can be calculated when rendering by firstName + lastName.
const [userName, setUserName] = useState({ firstName: "", lastName: "" });
const fullName = userName.firstName + " " + userName.lastName;
const handleUserNameChange = (e) => {
setUserName({ ...userName, [e.target.name]: e.target.value });
};
Enter fullscreen mode Exit fullscreen mode
And props should not be held in the state.
function Text({ children, color }) {
const [textColor] = useState(color);
return <h1 style={{ color: textColor }}>{children}</h1>;
}
export default function Example() {
const [color, setColor] = useState("red");
return (
<div>
<p>
Select Color
<select value={color} onChange={(e) => setColor(e.target.value)}>
<option value="red">Red</option>
<option value="blue">Blue</option>
<option value="green">Green</option>
</select>
</p>
<Text color={color}>Color will be changed</Text>
</div>
);
}
Enter fullscreen mode Exit fullscreen mode
The color seems to change at a glance, but doesn’t change.
Because useState is initialized when first rendering.
- Fixed
function Text({ children, color }) {
const textColor = color;
return <h1 style={{ color: textColor }}>{children}</h1>;
}
Enter fullscreen mode Exit fullscreen mode
4. Avoid declaring the same state multiple times
const initialItems = [
{ id: 1, title: "taskA" },
{ id: 2, title: "taskB" },
{ id: 3, title: "taskC" },
];
export default function TaskList() {
const [tasks, setTasks] = useState(initialItems);
const [selectedTask, setSelectedTask] = useState(tasks[0]);
function handleTaskChange(id, e) {
setTasks(tasks.map((task) => (task.id === id ? { ...task, title: e.target.value } : task)));
setSelectedTask((task) => (task.id === id ? { ...task, title: e.target.value } : task));
}
return (
<>
<h2>Task List</h2>
<ul>
{tasks.map((task, index) => (
<li key={task.id}>
<input
value={task.title}
onChange={(e) => {
handleTaskChange(task.id, e);
}}
/>
<button
onClick={() => {
setSelectedTask(task);
}}
>
Select
</button>
</li>
))}
</ul>
<p>Today's task {selectedTask.title}</p>
</>
);
}
Enter fullscreen mode Exit fullscreen mode
The task and the selectedTask handle the same data.
When you edit the task, you have to update selectedTask.
So, fix the codebase to calculate the selectedTask by id
function TaskList() {
const [tasks, setTasks] = useState(initialItems);
const [selectedTaskId, setSelectedTaskId] = useState(0);
function handleTaskChange(id, e) {
setTasks(tasks.map((task) => (task.id === id ? { ...task, title: e.target.value } : task)));
}
const selectedTask = tasks.find((task) => task.id === selectedTaskId);
return (
<>
<h2>Task List</h2>
<ul>
{tasks.map((task) => (
<li key={task.id}>
<input
value={task.title}
onChange={(e) => {
handleTaskChange(task.id, e);
}}
/>
<button
onClick={() => {
setSelectedTaskId(task.id);
}}
>
Select
</button>
</li>
))}
</ul>
<p>Today's Task {selectedTask.title}</p>
</>
);
}
Enter fullscreen mode Exit fullscreen mode
답글 남기기