-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
언제-함수를-Hooks의-의존성배열에-두어야할까요?, 당신의-코드를-즉시-향상시키는-4가지-React-팁들(24.06.03) #6
Merged
Merged
Changes from 4 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
c6925cd
docs: 당신의-코드를-즉시-향상시키는-4가지-React-팁들.md
Jaymyong66 870d516
docs: 언제-함수를-Hooks의-의존성배열에-두어야할까요?.md
Jaymyong66 a2b771e
Merge branch 'main' of https://github.com/Wannabe-Woowa-Article/2024-…
Jaymyong66 7d9df7f
refactor:: 폴더 이동, 파일명 영문으로 변경
Jaymyong66 4820cfb
피드백 반영(주석 번역 추가, 원문 링크 추가)
Jaymyong66 d2e1397
피드백 반영(효과->effect, re-ren에 큰 따옴표)
Jaymyong66 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 | ||
|
||
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: "", | ||
}); | ||
|
||
// First handler | ||
const handleNameChange = (e) => { | ||
setUser((prev) => ({ | ||
...prev, | ||
name: e.target.value, | ||
})); | ||
}; | ||
|
||
// Second handler! | ||
const handleSurnameChange = (e) => { | ||
setUser((prev) => ({ | ||
...prev, | ||
surname: e.target.value, | ||
})); | ||
}; | ||
|
||
// Third handler!!! | ||
const handleAddressChange = (e) => { | ||
setUser((prev) => ({ | ||
...prev, | ||
address: e.target.value, | ||
})); | ||
}; | ||
|
||
// What if we need one more input? Should we create another handler for it? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 사소하긴 한데 주석에 코드에 대한 설명이 적혀 있는 경우라면 주석도 번역해 주시면 좋을 것 같습니다~ |
||
|
||
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([]); | ||
|
||
// Handler to load data from the server | ||
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!" }); | ||
} | ||
}; | ||
|
||
// We want to fetch only on mount | ||
useEffect(() => { | ||
fetchList(); | ||
}, []); | ||
|
||
// Handler responsible for deleting items | ||
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!" }); | ||
} | ||
}; | ||
}; | ||
|
||
// Here we just render our data items | ||
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> | ||
); | ||
} | ||
Comment on lines
+291
to
+307
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오 꿀팁👍 |
||
``` | ||
|
||
[링크](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.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
번역 글을 보다가 원문의 의미가 궁금할 수도 있으니 원문으로 이동할 수 있는 링크 추가해 주시면 좋을 것 같아요~