Skip to content

Commit

Permalink
Merge pull request #6 from Wannabe-Woowa-Article/master-wi
Browse files Browse the repository at this point in the history
  • Loading branch information
llqqssttyy authored Jun 10, 2024
2 parents 4434468 + d2e1397 commit d71f81a
Show file tree
Hide file tree
Showing 2 changed files with 625 additions and 0 deletions.
365 changes: 365 additions & 0 deletions June/article/4-React-Tips-to-Instantly-Improve-Your-Code.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,365 @@
# [4 React Tips to Instantly Improve Your Code](https://javascript.plainenglish.io/4-react-tips-to-instantly-improve-your-code-7456e028cfa3)

Pavel Pogosov
Updated On Feb 2, 2023, Translate on May 31 2024
Keyword : **React**

React에 λŒ€ν•œ νƒ„νƒ„ν•œ 지식은 ν”„λ‘ νŠΈμ—”λ“œ κ°œλ°œμžμ—κ²Œ κ°€μž₯ κ°€μΉ˜ μžˆλŠ” 기술 쀑 ν•˜λ‚˜μž…λ‹ˆλ‹€. λ§Žμ€ 기업듀이 μ§€μ†μ μœΌλ‘œ React 개발자λ₯Ό μ°Ύκ³  있으며, κ·Έλ“€μ—κ²Œ 더 λ§Žμ€ 보수λ₯Ό μ§€λΆˆν•˜λ €κ³  ν•©λ‹ˆλ‹€. κ·Έλ ‡κΈ° λ•Œλ¬Έμ— κ°œλ°œμžλ‘œμ„œ μ§€μ†μ μœΌλ‘œ μ„±μž₯ν•˜λŠ” 것은 맀우 보람 μžˆλŠ” μΌμž…λ‹ˆλ‹€.

μ—¬λŸ¬λΆ„μ˜ 여정에 도움이 되기 μœ„ν•΄, μ œκ°€ 더 λ‚˜μ€ React μ½”λ“œλ₯Ό μž‘μ„±ν•˜λŠ” 데 도움이 λ˜μ—ˆλ˜ λ„€ 가지 νŒμ„ κ³΅μœ ν•˜κ³ μž ν•©λ‹ˆλ‹€. μƒˆλ‘œμš΄ 것을 배우고 μœ μš©ν•˜κ²Œ μ‚¬μš©ν•˜μ‹œκΈΈ λ°”λžλ‹ˆλ‹€. 그럼 λ°”λ‘œ μ‹œμž‘ν•΄ λ΄…μ‹œλ‹€!

### λͺ©μ°¨

- ν•Έλ“€λŸ¬μ—μ„œ ν•¨μˆ˜ λ°˜ν™˜ν•˜κΈ°
- μ±…μž„ 뢄리
- 쑰건문 λŒ€μ‹  객체 맡 μ‚¬μš©ν•˜κΈ°
- React lifecycle의 외뢀에 독립 λ³€μˆ˜ 두기

### ν•Έλ“€λŸ¬μ—μ„œ ν•¨μˆ˜ λ°˜ν™˜ν•˜κΈ°

ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ— μ΅μˆ™ν•˜λ‹€λ©΄ μ œκ°€ 무슨 말을 ν•˜λŠ”μ§€ μ•„μ‹€ κ²λ‹ˆλ‹€. μš°λ¦¬λŠ” 이λ₯Ό "[컀링](https://en.wikipedia.org/wiki/Currying)"이라고 λΆ€λ₯Ό 수 μžˆμŠ΅λ‹ˆλ‹€. 본질적으둜, 일뢀 ν•¨μˆ˜ λ§€κ°œλ³€μˆ˜λ₯Ό 미리 μ„€μ •ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

μ•„λž˜ λ³΄μΌλŸ¬ν”Œλ ˆμ΄νŠΈ μ½”λ“œμ—μ„œ λͺ…μ‹œμ μΈ λ¬Έμ œκ°€ μžˆμŠ΅λ‹ˆλ‹€. 이 기술이 도움이 될 κ²ƒμž…λ‹ˆλ‹€!

```tsx
export default function App() {
const [user, setUser] = useState({
name: "",
surname: "",
address: "",
});

// 첫 ν•Έλ“€λŸ¬
const handleNameChange = (e) => {
setUser((prev) => ({
...prev,
name: e.target.value,
}));
};

// λ‘λ²ˆμ§Έ ν•Έλ“€λŸ¬!
const handleSurnameChange = (e) => {
setUser((prev) => ({
...prev,
surname: e.target.value,
}));
};

// μ„Έλ²ˆμ§Έ ν•Έλ“€λŸ¬!!!
const handleAddressChange = (e) => {
setUser((prev) => ({
...prev,
address: e.target.value,
}));
};

// λ§Œμ•½ ν•˜λ‚˜μ˜ input이 더 ν•„μš”ν•˜λ‹€λ©΄ μ–΄λ–¨κΉŒμš”? 이λ₯Ό μœ„ν•œ 또 λ‹€λ₯Έ ν•Έλ“€λŸ¬λ₯Ό λ§Œλ“€μ–΄μ•Όν• κΉŒμš”?

return (
<>
<input value={user.name} onChange={handleNameChange} />
<input value={user.surname} onChange={handleSurnameChange} />
<input value={user.address} onChange={handleAddressChange} />
</>
);
}
```

**Solution**

```tsx
export default function App() {
const [user, setUser] = useState({
name: "",
surname: "",
address: "",
});

const handleInputChange = (field) => {
return (e) => {
setUser((prev) => ({
...prev,
[field]: e.target.value,
}));
};
};

return (
<>
<input value={user.name} onChange={handleInputChange("name")} />
<input value={user.surname} onChange={handleInputChange("surname")} />
<input value={user.address} onChange={handleInputChange("address")} />

{JSON.stringify(user)}
</>
);
}
```

https://medium.com/@pashkapag/5-react-usestate-mistakes-that-will-get-you-fired-b342289debfe

### μ±…μž„ 뢄리

"God" μ»΄ν¬λ„ŒνŠΈλ₯Ό λ§Œλ“œλŠ” 것은 κ°œλ°œμžλ“€μ΄ ν”νžˆ 저지λ₯΄λŠ” μ‹€μˆ˜μž…λ‹ˆλ‹€. "God" μ»΄ν¬λ„ŒνŠΈλΌκ³  λΆˆλ¦¬λŠ” μ΄μœ λŠ” μ΄ν•΄ν•˜κ³  μœ μ§€ λ³΄μˆ˜ν•˜κΈ° μ–΄λ €μš΄ λ§Žμ€ μ½”λ“œ 라인을 ν¬ν•¨ν•˜κ³  있기 λ•Œλ¬Έμž…λ‹ˆλ‹€. 독립적인 ν•˜μœ„ λͺ¨λ“ˆ μ„ΈνŠΈλ‘œ μ»΄ν¬λ„ŒνŠΈλ₯Ό λ‚˜λˆ„λŠ” 것을 κ°•λ ₯히 ꢌμž₯ν•©λ‹ˆλ‹€.

일반적인 κ΅¬μ‘°λŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€:

- **UI λͺ¨λ“ˆ**: μ‹œκ°μ  ν‘œν˜„λ§Œ λ‹΄λ‹Ήν•©λ‹ˆλ‹€.
- **둜직/λͺ¨λΈ λͺ¨λ“ˆ**: λΉ„μ¦ˆλ‹ˆμŠ€ 둜직만 ν¬ν•¨ν•©λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, μ»€μŠ€ν…€ 훅이 여기에 ν•΄λ‹Ήν•©λ‹ˆλ‹€.
- **Lib λͺ¨λ“ˆ**: μ»΄ν¬λ„ŒνŠΈμ— ν•„μš”ν•œ λͺ¨λ“  μœ ν‹Έλ¦¬ν‹°λ₯Ό ν¬ν•¨ν•©λ‹ˆλ‹€.

이 κ°œλ…μ„ μ„€λͺ…ν•˜κΈ° μœ„ν•œ μž‘μ€ 데λͺ¨ 예제λ₯Ό λ³΄μ—¬λ“œλ¦¬κ² μŠ΅λ‹ˆλ‹€.

```tsx
export function ListComponent() {
// Our local state
const [list, setList] = useState([]);

// μ„œλ²„λ‘œλΆ€ν„° 데이터λ₯Ό λ‘œλ“œν•˜κΈ°μœ„ν•œ ν•Έλ“€λŸ¬
const fetchList = async () => {
try {
const resp = await fetch("https://www.url.com/list");
const data = await resp.json();

setList(data);
} catch {
showAlert({ text: "Something went wrong!" });
}
};

// 마운트될 λ•Œλ§Œ fetchν•˜κΈΈ μ›ν•˜κΈ°μ—
useEffect(() => {
fetchList();
}, []);

// μ•„μ΄ν…œμ„ μ§€μš°λŠ” μ—­ν• μ˜ ν•Έλ“€λŸ¬
const handleDeleteItem = (id) => {
return () => {
try {
fetch(`https://www.url.com/list/${id}`, {
method: "DELETE",
});
setList((prev) => prev.filter((x) => x.id !== id));
} catch {
showAlert({ text: "Something went wrong!" });
}
};
};

// μ—¬κΈ°μ„œ 데이터 μ•„μ΄ν…œμ„ renderν•œλ‹€
return (
<div className="list-component">
{list.map(({ id, name }) => (
<div key={id} className="list-component__item>">
{/* We want to trim long name with ellipsis */}
{name.slice(0, 30) + (name.length > 30 ? "..." : "")}

<div onClick={handleDeleteItem(id)} className="list-component__icon">
<DeleteIcon />
</div>
</div>
))}
</div>
);
}
```

μš°λ¦¬λŠ” λͺ¨λΈκ³Ό UI λͺ¨λ“ˆμ—μ„œ μ‚¬μš©ν•  μœ ν‹Έλ¦¬ν‹°λ₯Ό μž‘μ„±ν•˜λŠ” 것뢀터 μ‹œμž‘ν•΄μ•Ό ν•©λ‹ˆλ‹€.

```tsx
export async function getList(onSuccess) {
try {
const resp = await fetch("https://www.url.com/list");
const data = await resp.json();

onSuccess(data);
} catch {
showAlert({ text: "Something went wrong!" });
}
}

export async function deleteListItem(id, onSuccess) {
try {
fetch(`https://www.url.com/list/${id}`, {
method: "DELETE",
});
onSuccess();
} catch {
showAlert({ text: "Something went wrong!" });
}
}

export function trimName(name) {
return name.slice(0, 30) + (name.lenght > 30 ? "..." : "");
}
```

이제 λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ„ κ΅¬ν˜„ν•΄μ•Ό ν•©λ‹ˆλ‹€. 이λ₯Ό μœ„ν•΄ ν•„μš”ν•œ 것듀을 λ°˜ν™˜ν•˜λŠ” μ»€μŠ€ν…€ 훅을 μž‘μ„±ν•˜λ©΄ λ©λ‹ˆλ‹€.

```tsx
export function useList() {
const [list, setList] = useState([]);

const handleDeleteItem = useCallback((id) => {
return () => {
deleteListItem(id, () => {
setList((prev) => prev.filter((x) => x.id !== id));
});
};
}, []);

useEffect(() => {
getList(setList);
}, []);

return useMemo(
() => ({
list,
handleDeleteItem,
}),
[list, handleDeleteItem]
);
}
```

λ§ˆμ§€λ§‰ λ‹¨κ³„λŠ” UI λͺ¨λ“ˆμ„ μž‘μ„±ν•œ ν›„ λͺ¨λ“  것을 ν•¨κ»˜ κ²°ν•©ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

```tsx
export function ListComponentItem({ name, onDelete }) {
return (
<div className="list-component__item>">
{trimName(name)}

<div onClick={onDelete} className="list-component__icon">
<DeleteIcon />
</div>
</div>
);
}

export function ListComponent() {
const { list, handleDeleteItem } = useList();

return (
<div className="list-component">
{list.map(({ id, name }) => (
<ListComponentItem
key={id}
name={name}
onDelete={handleDeleteItem(id)}
/>
))}
</div>
);
}
```

### 쑰건문 λŒ€μ‹  객체 맡 μ‚¬μš©ν•˜κΈ°

λ³€μˆ˜μ— 따라 λ‹€μ–‘ν•œ μš”μ†Œλ₯Ό ν‘œμ‹œν•΄μ•Ό ν•  경우, 이 νŒμ„ κ΅¬ν˜„ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 이 κ°„λ‹¨ν•œ μ „λž΅μ„ μ‚¬μš©ν•˜λ©΄ μ»΄ν¬λ„ŒνŠΈλ₯Ό 더 μ„ μ–Έμ μœΌλ‘œ λ§Œλ“€κ³  μ½”λ“œ 이해λ₯Ό λ‹¨μˆœν™”ν•  수 μžˆμŠ΅λ‹ˆλ‹€. κ²Œλ‹€κ°€ κΈ°λŠ₯을 ν™•μž₯ν•˜λŠ” 것도 더 μ‰¬μ›Œμ§‘λ‹ˆλ‹€.

**Problem**

```tsx
function Account({ type }) {
let Component = UsualAccount;

if (type === "vip") {
Component = VipAccount;
}

if (type === "moderator") {
Component = ModeratorAccount;
}

if (type === "admin") {
Component = AdminAccount;
}

return (
<div className="account">
<Component />
<AccountStatistics />
</div>
);
}
```

**Solution**

```tsx
const ACCOUNTS_MAP = {
vip: VipAccount,
usual: UsualAccount,
admin: AdminAccount,
moderator: ModeratorAccount,
};

function Account({ type }) {
const Component = ACCOUNTS_MAP[type];

return (
<div className="account">
<Component />
<AccountStatistics />
</div>
);
}
```

[링크](http://javascript.plainenglish.io/frontend-architectures-classic-approach-no-architecture-d3c839e46403?source=post_page-----7456e028cfa3--------------------------------)

### React lifecycle의 외뢀에 독립 λ³€μˆ˜ 두기

μ•„μ΄λ””μ–΄λŠ” React μ»΄ν¬λ„ŒνŠΈ 생λͺ…μ£ΌκΈ° λ©”μ„œλ“œλ₯Ό ν•„μš”λ‘œ ν•˜μ§€ μ•ŠλŠ” λ‘œμ§μ„ μ»΄ν¬λ„ŒνŠΈ μžμ²΄μ—μ„œ λΆ„λ¦¬ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€. μ΄λ ‡κ²Œ ν•˜λ©΄ 쒅속성을 더 λͺ…ν™•ν•˜κ²Œ λ§Œλ“€μ–΄ μ½”λ“œμ˜ λͺ…확성을 ν–₯μƒμ‹œν‚΅λ‹ˆλ‹€. λ”°λΌμ„œ μ»΄ν¬λ„ŒνŠΈλ₯Ό 읽고 μ΄ν•΄ν•˜λŠ” 것이 훨씬 μ‰¬μ›Œμ§‘λ‹ˆλ‹€.

**Problem**

```tsx
function useItemsList() {
const defaultItems = [1, 2, 3, 4, 5];
const [items, setItems] = useState(defaultItems);

const toggleArrayItem = (arr, val) => {
return arr.includes(val) ? arr.filter((el) => el !== val) : [...arr, val];
};

const handleToggleItem = (num) => {
return () => {
setItems(toggleArrayItem(items, num));
};
};

return {
items,
handleToggleItem,
};
}
```

**Solution**

```tsx
const DEFAULT_ITEMS = [1, 2, 3, 4, 5];

const toggleArrayItem = (arr, val) => {
return arr.includes(val) ? arr.filter((el) => el !== val) : [...arr, val];
};

function useItemsList() {
const [items, setItems] = useState(DEFAULT_ITEMS);

const handleToggleItem = (num) => {
return () => {
setItems(toggleArrayItem(items, num));
};
};

return {
items,
handleToggleItem,
};
}
```

**Thanks for reading!**
Loading

0 comments on commit d71f81a

Please sign in to comment.