-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #6 from Wannabe-Woowa-Article/master-wi
- Loading branch information
Showing
2 changed files
with
625 additions
and
0 deletions.
There are no files selected for viewing
365 changes: 365 additions & 0 deletions
365
June/article/4-React-Tips-to-Instantly-Improve-Your-Code.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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!** |
Oops, something went wrong.