From c6925cd74837246e6d61d7d5aa0dcb9457536a2c Mon Sep 17 00:00:00 2001 From: jayming66 Date: Mon, 3 Jun 2024 14:36:55 +0900 Subject: [PATCH 1/5] =?UTF-8?q?docs:=20=EB=8B=B9=EC=8B=A0=EC=9D=98-?= =?UTF-8?q?=EC=BD=94=EB=93=9C=EB=A5=BC-=EC=A6=89=EC=8B=9C-=ED=96=A5?= =?UTF-8?q?=EC=83=81=EC=8B=9C=ED=82=A4=EB=8A=94-4=EA=B0=80=EC=A7=80-React-?= =?UTF-8?q?=ED=8C=81=EB=93=A4.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...247\200-React-\355\214\201\353\223\244.md" | 365 ++++++++++++++++++ 1 file changed, 365 insertions(+) create mode 100644 "May/article/\353\213\271\354\213\240\354\235\230-\354\275\224\353\223\234\353\245\274-\354\246\211\354\213\234-\355\226\245\354\203\201\354\213\234\355\202\244\353\212\224-4\352\260\200\354\247\200-React-\355\214\201\353\223\244.md" diff --git "a/May/article/\353\213\271\354\213\240\354\235\230-\354\275\224\353\223\234\353\245\274-\354\246\211\354\213\234-\355\226\245\354\203\201\354\213\234\355\202\244\353\212\224-4\352\260\200\354\247\200-React-\355\214\201\353\223\244.md" "b/May/article/\353\213\271\354\213\240\354\235\230-\354\275\224\353\223\234\353\245\274-\354\246\211\354\213\234-\355\226\245\354\203\201\354\213\234\355\202\244\353\212\224-4\352\260\200\354\247\200-React-\355\214\201\353\223\244.md" new file mode 100644 index 0000000..883923e --- /dev/null +++ "b/May/article/\353\213\271\354\213\240\354\235\230-\354\275\224\353\223\234\353\245\274-\354\246\211\354\213\234-\355\226\245\354\203\201\354\213\234\355\202\244\353\212\224-4\352\260\200\354\247\200-React-\355\214\201\353\223\244.md" @@ -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? + + return ( + <> + + + + + ); +} +``` + +**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 ( + <> + + + + + {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 ( +
+ {list.map(({ id, name }) => ( +
+ {/* We want to trim long name with ellipsis */} + {name.slice(0, 30) + (name.length > 30 ? "..." : "")} + +
+ +
+
+ ))} +
+ ); +} +``` + +우리는 모델과 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 ( +
+ {trimName(name)} + +
+ +
+
+ ); +} + +export function ListComponent() { + const { list, handleDeleteItem } = useList(); + + return ( +
+ {list.map(({ id, name }) => ( + + ))} +
+ ); +} +``` + +### 조건문 대신 객체 맵 사용하기 + +변수에 따라 다양한 요소를 표시해야 할 경우, 이 팁을 구현할 수 있습니다. 이 간단한 전략을 사용하면 컴포넌트를 더 선언적으로 만들고 코드 이해를 단순화할 수 있습니다. 게다가 기능을 확장하는 것도 더 쉬워집니다. + +**Problem** + +```tsx +function Account({ type }) { + let Component = UsualAccount; + + if (type === "vip") { + Component = VipAccount; + } + + if (type === "moderator") { + Component = ModeratorAccount; + } + + if (type === "admin") { + Component = AdminAccount; + } + + return ( +
+ + +
+ ); +} +``` + +**Solution** + +```tsx +const ACCOUNTS_MAP = { + vip: VipAccount, + usual: UsualAccount, + admin: AdminAccount, + moderator: ModeratorAccount, +}; + +function Account({ type }) { + const Component = ACCOUNTS_MAP[type]; + + return ( +
+ + +
+ ); +} +``` + +[링크](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!** From 870d51683f9dfe1b384abdbbb34c153bd6079a03 Mon Sep 17 00:00:00 2001 From: jayming66 Date: Mon, 3 Jun 2024 14:37:02 +0900 Subject: [PATCH 2/5] =?UTF-8?q?docs:=20=EC=96=B8=EC=A0=9C-=ED=95=A8?= =?UTF-8?q?=EC=88=98=EB=A5=BC-Hooks=EC=9D=98-=EC=9D=98=EC=A1=B4=EC=84=B1?= =?UTF-8?q?=EB=B0=B0=EC=97=B4=EC=97=90-=EB=91=90=EC=96=B4=EC=95=BC?= =?UTF-8?q?=ED=95=A0=EA=B9=8C=EC=9A=94=3F.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...4\355\225\240\352\271\214\354\232\224?.md" | 260 ++++++++++++++++++ 1 file changed, 260 insertions(+) create mode 100644 "May/article/\354\226\270\354\240\234-\355\225\250\354\210\230\353\245\274-Hooks\354\235\230-\354\235\230\354\241\264\354\204\261\353\260\260\354\227\264\354\227\220-\353\221\220\354\226\264\354\225\274\355\225\240\352\271\214\354\232\224?.md" diff --git "a/May/article/\354\226\270\354\240\234-\355\225\250\354\210\230\353\245\274-Hooks\354\235\230-\354\235\230\354\241\264\354\204\261\353\260\260\354\227\264\354\227\220-\353\221\220\354\226\264\354\225\274\355\225\240\352\271\214\354\232\224?.md" "b/May/article/\354\226\270\354\240\234-\355\225\250\354\210\230\353\245\274-Hooks\354\235\230-\354\235\230\354\241\264\354\204\261\353\260\260\354\227\264\354\227\220-\353\221\220\354\226\264\354\225\274\355\225\240\352\271\214\354\232\224?.md" new file mode 100644 index 0000000..10d52e2 --- /dev/null +++ "b/May/article/\354\226\270\354\240\234-\355\225\250\354\210\230\353\245\274-Hooks\354\235\230-\354\235\230\354\241\264\354\204\261\353\260\260\354\227\264\354\227\220-\353\221\220\354\226\264\354\225\274\355\225\240\352\271\214\354\232\224?.md" @@ -0,0 +1,260 @@ +# [When do I use functions in a Hooks Dependency Array?](https://reacttraining.com/blog/when-to-use-functions-in-hooks-dependency-array) + +Brad Westfall +Updated On Sep 30, 2019, Translate on Jun 3, 2024 +Keyword : **React** **Hooks** + +### 간단히 말하자면... + +**linter가 당신에게 그렇게 하라고 할때마다 해야합니다.** +[그리고 이것은 꽤 자주 일어납니다.](https://legacy.reactjs.org/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies) +하지만 왜 linter는 때때로 그렇게 하라고 하고, 항상 그렇지는 않을까요? + +--- + +Hooks는 React에 함수형 구성을 도입하기 위해 설계되었지만, 제대로 작동하기 위해 따라야 할 몇 가지 규칙이 있습니다. React에는 특정 작업을 잘못할 때(예: 훅의 순서를 조건부로 변경하는 경우) 알려주는 몇 가지 내장 린팅 규칙이 있습니다. [또한 별도로 설치해야 하는 추가 규칙도 있습니다.](https://www.npmjs.com/package/eslint-plugin-react-hooks) 이러한 추가 규칙은 의존성 배열에 무엇을 추가해야 하는지 알려주는 [exhaustive-deps](https://github.com/facebook/react/issues/14920) 규칙과 같이 버그를 조기에 잡는 데 도움이 됩니다. + +의존성 배열 개념을 사용하는 훅은 여러 가지가 있지만, 아마도 많은 사람들이 처음 배우는 훅 중 하나인 useEffect를 배우면서 의존성 배열에 대해 알게 될 것입니다: + +```jsx +function ComposeMessage() { + const [message, setMessage] = useState(); + + // 메시지가 변경될 때마다 제목을 변경하는 것은 side-effects입니다. + // 그래서 `useEffect`가 필요합니다. + useEffect(() => { + document.title = message; + }, [message]); + + return ( +
+ setMessage(e.target.value)} /> +
+ ); +} +``` + +우리의 효과는 message 상태에 "의존"합니다. 따라서 그 상태가 변경되면 효과를 다시 실행해야 합니다. + +이제 메시지가 변경될 때 메시지를 로컬 저장소에 저장해 보겠습니다. 이렇게 하면 메시지가 저장되지 않았을 때 신속하게 초안으로 복구할 수 있습니다. 또한 수신자의 사용자 ID를 나타내는 uid prop도 사용하겠습니다. + +```jsx +import { saveToLocalStorage, getFromLocalStorage } from "./saveToLocalStorage"; + +function ComposeMessage({ uid }) { + const [message, setMessage] = useState(getFromLocalStorage(uid) || ""); + + useEffect(() => { + saveToLocalStorage(uid, message); + }, [uid, message]); // 우리의 effect는 이제 더 많은 것에 의존하고 있습니다. + + return ( + setMessage(e.target.value)} + /> + ); +} +``` + +linter는 이제 uid가 side effect의 일부이기 때문에 의존성 배열에 uid를 추가하라고 요청할 것입니다. + +> 그러나 'uid'는 상태가 아닌 props 입니다 ! + +자, 괜찮습니다. 비록 그것이 우리의 컴포넌트 상태는 아니지만, 다른 곳(예: 부모 컴포넌트)의 상태이므로 동일한 개념입니다. + +그렇다면 함수 `saveToLocalStorage`는 어떻게 할까요? 효과에서 이를 사용하고 있으니 의존성 배열에 추가해야 할까요? + +이 경우에는 아닙니다. 왜 그런지 논의하기 전에, `saveToLocalStorage`가 prop으로 전달되는 아래의 리팩터링된 코드와 비교해 봅시다. + +```jsx +function ComposeMessage({ uid, defaultMessage, saveToLocalStorage }) { + const [message, setMessage] = useState(defaultMessage || ""); + + useEffect(() => { + saveToLocalStorage(uid, message); + }, [uid, message, saveToLocalStorage]); // Now it goes here + + return ( + setMessage(e.target.value)} + /> + ); +} +``` + +이제 linter가 saveToLocalStorage를 의존성 배열에 추가하라고 요청합니다. 차이점은 무엇일까요? + +궁극적으로, React는 effects 내의 상태가 변경될 경우 effect를 re-run 해야 합니다. 이전에 `saveToLocalStorage`가 import 되었을 때, linter는 그 함수가 컴포넌트 상태를 "close over"하여 변경되었을 때 effect를 다시 실행할 필요가 없다는 것을 알고 있습니다. 그러나 `saveToLocalStorage`가 prop일 때, 린터는 부모 컴포넌트가 `ComposeMessage`를 어떻게 구현할지에 대해 충분히 알지 못합니다. 다시 말해, linter는 전체 앱을 탐색하여 `ComposeMessage`가 사용되는 모든 위치와 부모가 prop을 전달하는 방식을 알지 못합니다. 그리고 설령 그렇게 한다 하더라도, linter는 당신이 미래에 그것을 어떻게 사용할지 의도를 알지 못합니다. 이러한 불확실성 때문에, 린터는 이제 `saveToLocalStorage`를 의존성 배열에 추가하라고 요청합니다. + +다음은 부모 컴포넌트가 구현될 수 있는 한 가지 예입니다: + +```jsx +import { saveToLocalStorage, getFromLocalStorage } from "./saveToLocalStorage"; + +function UserProfile({ uid }) { + return ( + + ); +} +``` + +비록 `saveToLocalStorage`가 여전히 import에 대한 참조일 뿐이지만, 자식 `ComposeMessage`는 prop이 의존성 배열에 추가되어야 한다고 말하고 있습니다. 다시 말하지만, 이제 중요한 차이점은 **확실성**입니다. 이전에는 React가 `saveToLocalStorage`가 어떤 상태도 close over하지 않는다는 것을 알고 있었습니다. 부모를 리팩터링한다면 어떻게 될까요? + +부모 컴포넌트에 애플리케이션 세부 정보를 유지하고 `ComposeMessage`는 저장해야 할 시점만 보고하고 `uid`와 같은 것에 대해 알 필요가 없다고 가정해 봅시다. 이 경우 코드가 다음과 같이 보일 수 있습니다: + +```jsx +// UserProfile.js +import ComposeMessage from "./ComposeMessage"; +import { saveToLocalStorage, getFromLocalStorage } from "./saveToLocalStorage"; + +function UserProfile({ uid }) { + return ( + saveToLocalStorage(uid, message)} + /> + ); +} + +// ComposeMessage.js +function ComposeMessage({ defaultMessage, saveToLocalStorage }) { + const [message, setMessage] = useState(defaultMessage || ""); + + useEffect(() => { + saveToLocalStorage(message); + }, [message, saveToLocalStorage]); + + return ( + setMessage(e.target.value)} + /> + ); +} +``` + +저장하기 위해 실제 함수로 전달되는 `saveToLocalStorage`는 import된 `saveToLocalStorage`를 래핑하는 화살표 함수입니다. 이 리팩터링을 통해 전달된 함수는 `uid`를 close over하게 되므로, 이제 변경될 가능성이 있기 때문에 `ComposeMessage`에서 `saveToLocalStorage` prop을 의존성 배열에 포함하는 것이 중요합니다. React는 이 함수가 필요할지 여부를 알 수 없으므로 항상 포함시키도록 했습니다. + +다른 고려 사항: + +부모 컴포넌트가 어떤 이유로든 다시 렌더링되면(예: 상태 변경 또는 새로운 props), 화살표 함수는 다시 렌더링될 때마다 새로 생성됩니다. 이는 부모가 다시 렌더링될 때마다 `saveToLocalStorage` prop의 함수 아이덴티티가 변경된다는 것을 의미합니다. `ComposeMessage`에서는 `saveToLocalStorage`를 의존성 배열에 포함시켜 버그를 방지해야 하지만, 부모가 다시 렌더링될 때마다 그 아이덴티티가 변경되므로 불필요하게 로컬 저장소에 저장하게 됩니다. 이를 [시연하는 예제](https://codesandbox.io/s/funny-taussig-5tont)를 보세요. + +로컬 저장소에 너무 자주 저장하는 것은 괜찮을 수 있지만, 부수 효과가 네트워크 요청인 경우에는 이를 피하고 싶을 것입니다. 따라서 함수의 아이덴티티가 변하지 않도록 유지할 필요가 있습니다. + +### `useCallback` 사용하기 + +`uid`와 함수의 아이덴티티를 동기화하기 위해 부모 컴포넌트의 함수를 다음과 같이 구현할 수 있습니다: + +```jsx +import ComposeMessage from "./ComposeMessage"; +import { saveToLocalStorage, getFromLocalStorage } from "./saveToLocalStorage"; + +function UserProfile({ uid }) { + const save = useCallback( + (message) => { + saveToLocalStorage(uid, message); + }, + [uid] + ); + + return ( + + ); +} +``` + +`useCallback` 훅은 이와 같은 목적을 위해 함수의 메모이제이션 버전을 생성합니다. 이는 또 다른 의존성 배열 개념을 가진 훅입니다. 이 경우 `save` 함수는 의존성 배열 내의 무언가가 변경되지 않는 한, `UserProfil`e이 몇 번이나 다시 렌더링되더라도 동일한 아이덴티티를 유지합니다. 유일한 예외는 의존성 배열의 무언가가 변경될 때 새로운 아이덴티티가 생성되는 것입니다. + +시나리오를 통해 설명해 보겠습니다: + +- 부모 컴포넌트 `UserProfile`이 `ComposeMessage`에 함수 prop을 전달합니다. +- `ComposeMessage`에서 효과(effect)를 다시 실행시키는 두 가지 조건은 다음과 같습니다: + + - 메시지가 변경될 때 (우리가 원하는 것) + - 또는 `saveToLocalStorage` prop이 변경될 때 (이 점을 염두에 두세요) + +- 부모 함수는 리렌더링을 경험할 수 있으며 이는 `ComposeMessage`를 다시 렌더링하게 합니다. 이 경우 useEffect의 의존성 배열이 다시 평가됩니다. 부모가 다시 렌더링되지만 `uid`가 변경되지 않는 경우, `saveToLocalStorage` 함수가 변경되지 않도록 하여 effect가 실행되지 않도록 해야 합니다. `useCallback`이 이를 해결해줍니다. +- 부모의 `uid`가 변경되면, `useCallback`은 `save`를 위한 새로운 아이덴티티를 생성하고, 따라서 `uid`가 변경될 때 이후의 effect가 다시 실행됩니다. + +## 요약 + +따라서 함수가 의존성 배열에 포함되어야 하는 경우는 언제일까요? 상태를 잠재적으로 close over 할 수 있을 때입니다. + +이 예제가 이를 완벽하게 요약해 줍니다: + +```jsx +const MyComponent = () => { + // 이 함수는 이 순간에 state를 close over하지 않습니다. + function logData() { + console.log("logData"); + } + + useEffect(() => { + logData(); + }, []); // `logData`는 의존성 배열 안으로 들어가지 않아도 됩니다. + + // ... +}; +``` + +그렇다면 어떤 props를 `console.log` 해봅시다. + +```jsx +const MyComponent = ({ data }) => { + // 이 함수는 이제 상태를 close over 합니다.(props는 어떤 것의 상태임을 기억하세요) + function logData() { + console.log(data); + } + + useEffect(() => { + logData(); + }, [logData]); // 이제 이것을 여기에 추가하세요 + + // ... +}; +``` + +이제 `logData`가 의존성 배열에 포함되었으므로, 새로운 문제는 `MyComponent`가 리렌더링될 때마다 이 함수가 변경된다는 점입니다. 따라서 `useCallback`을 사용해야 합니다: + +```jsx +const MyComponent = ({ data }) => { + const logData = useCallback(() => { + console.log(data); + }, [data]); + + useEffect(() => { + logData(); + }, [logData]); // 이제 이것을 여기에 추가하세요 + + // ... +}; +``` + +또는 이렇게 할 수 있습니다. + +```jsx +const MyComponent = ({ data }) => { + useEffect(() => { + function logData() { + console.log(data); + } + logData(); + }, [data]); // 이제 오직 `data`만 이것에 필요합니다. + + // ... +}; +``` + +`logData`는 상태를 close over하지만, 그것 자체가 effect의 일부이므로 배열에는 `data`만 필요합니다. From 7d9df7f5b08e0337239e94e820b84197ddf427d8 Mon Sep 17 00:00:00 2001 From: jayming66 Date: Mon, 3 Jun 2024 15:17:39 +0900 Subject: [PATCH 3/5] =?UTF-8?q?refactor::=20=ED=8F=B4=EB=8D=94=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99,=20=ED=8C=8C=EC=9D=BC=EB=AA=85=20=EC=98=81=EB=AC=B8?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../article/4-React-Tips-to-Instantly-Improve-Your-Code.md | 0 .../When-do-I-use-functions-in-a-Hooks-Dependency-Array?.md | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename "May/article/\353\213\271\354\213\240\354\235\230-\354\275\224\353\223\234\353\245\274-\354\246\211\354\213\234-\355\226\245\354\203\201\354\213\234\355\202\244\353\212\224-4\352\260\200\354\247\200-React-\355\214\201\353\223\244.md" => June/article/4-React-Tips-to-Instantly-Improve-Your-Code.md (100%) rename "May/article/\354\226\270\354\240\234-\355\225\250\354\210\230\353\245\274-Hooks\354\235\230-\354\235\230\354\241\264\354\204\261\353\260\260\354\227\264\354\227\220-\353\221\220\354\226\264\354\225\274\355\225\240\352\271\214\354\232\224?.md" => June/article/When-do-I-use-functions-in-a-Hooks-Dependency-Array?.md (100%) diff --git "a/May/article/\353\213\271\354\213\240\354\235\230-\354\275\224\353\223\234\353\245\274-\354\246\211\354\213\234-\355\226\245\354\203\201\354\213\234\355\202\244\353\212\224-4\352\260\200\354\247\200-React-\355\214\201\353\223\244.md" b/June/article/4-React-Tips-to-Instantly-Improve-Your-Code.md similarity index 100% rename from "May/article/\353\213\271\354\213\240\354\235\230-\354\275\224\353\223\234\353\245\274-\354\246\211\354\213\234-\355\226\245\354\203\201\354\213\234\355\202\244\353\212\224-4\352\260\200\354\247\200-React-\355\214\201\353\223\244.md" rename to June/article/4-React-Tips-to-Instantly-Improve-Your-Code.md diff --git "a/May/article/\354\226\270\354\240\234-\355\225\250\354\210\230\353\245\274-Hooks\354\235\230-\354\235\230\354\241\264\354\204\261\353\260\260\354\227\264\354\227\220-\353\221\220\354\226\264\354\225\274\355\225\240\352\271\214\354\232\224?.md" b/June/article/When-do-I-use-functions-in-a-Hooks-Dependency-Array?.md similarity index 100% rename from "May/article/\354\226\270\354\240\234-\355\225\250\354\210\230\353\245\274-Hooks\354\235\230-\354\235\230\354\241\264\354\204\261\353\260\260\354\227\264\354\227\220-\353\221\220\354\226\264\354\225\274\355\225\240\352\271\214\354\232\224?.md" rename to June/article/When-do-I-use-functions-in-a-Hooks-Dependency-Array?.md From 4820cfb9c5fa88906fcc7770036b2be850f8f65c Mon Sep 17 00:00:00 2001 From: MYONG JAEWI <78201530+Jaymyong66@users.noreply.github.com> Date: Mon, 10 Jun 2024 12:26:33 +0900 Subject: [PATCH 4/5] =?UTF-8?q?=ED=94=BC=EB=93=9C=EB=B0=B1=20=EB=B0=98?= =?UTF-8?q?=EC=98=81(=EC=A3=BC=EC=84=9D=20=EB=B2=88=EC=97=AD=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80,=20=EC=9B=90=EB=AC=B8=20=EB=A7=81=ED=81=AC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...eact-Tips-to-Instantly-Improve-Your-Code.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/June/article/4-React-Tips-to-Instantly-Improve-Your-Code.md b/June/article/4-React-Tips-to-Instantly-Improve-Your-Code.md index 883923e..989eb6e 100644 --- a/June/article/4-React-Tips-to-Instantly-Improve-Your-Code.md +++ b/June/article/4-React-Tips-to-Instantly-Improve-Your-Code.md @@ -1,4 +1,4 @@ -# 4 React Tips to Instantly Improve Your Code +# [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 @@ -29,7 +29,7 @@ export default function App() { address: "", }); - // First handler + // 첫 핸들러 const handleNameChange = (e) => { setUser((prev) => ({ ...prev, @@ -37,7 +37,7 @@ export default function App() { })); }; - // Second handler! + // 두번째 핸들러! const handleSurnameChange = (e) => { setUser((prev) => ({ ...prev, @@ -45,7 +45,7 @@ export default function App() { })); }; - // Third handler!!! + // 세번째 핸들러!!! const handleAddressChange = (e) => { setUser((prev) => ({ ...prev, @@ -53,7 +53,7 @@ export default function App() { })); }; - // What if we need one more input? Should we create another handler for it? + // 만약 하나의 input이 더 필요하다면 어떨까요? 이를 위한 또 다른 핸들러를 만들어야할까요? return ( <> @@ -115,7 +115,7 @@ 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"); @@ -127,12 +127,12 @@ export function ListComponent() { } }; - // We want to fetch only on mount + // 마운트될 때만 fetch하길 원하기에 useEffect(() => { fetchList(); }, []); - // Handler responsible for deleting items + // 아이템을 지우는 역할의 핸들러 const handleDeleteItem = (id) => { return () => { try { @@ -146,7 +146,7 @@ export function ListComponent() { }; }; - // Here we just render our data items + // 여기서 데이터 아이템을 render한다 return (
{list.map(({ id, name }) => ( From d2e139722ca4ac22d3f19bec9be82d6039fcba56 Mon Sep 17 00:00:00 2001 From: MYONG JAEWI <78201530+Jaymyong66@users.noreply.github.com> Date: Mon, 10 Jun 2024 12:28:54 +0900 Subject: [PATCH 5/5] =?UTF-8?q?=ED=94=BC=EB=93=9C=EB=B0=B1=20=EB=B0=98?= =?UTF-8?q?=EC=98=81(=ED=9A=A8=EA=B3=BC->effect,=20re-ren=EC=97=90=20?= =?UTF-8?q?=ED=81=B0=20=EB=94=B0=EC=98=B4=ED=91=9C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...hen-do-I-use-functions-in-a-Hooks-Dependency-Array?.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/June/article/When-do-I-use-functions-in-a-Hooks-Dependency-Array?.md b/June/article/When-do-I-use-functions-in-a-Hooks-Dependency-Array?.md index 10d52e2..fb484cc 100644 --- a/June/article/When-do-I-use-functions-in-a-Hooks-Dependency-Array?.md +++ b/June/article/When-do-I-use-functions-in-a-Hooks-Dependency-Array?.md @@ -34,7 +34,7 @@ function ComposeMessage() { } ``` -우리의 효과는 message 상태에 "의존"합니다. 따라서 그 상태가 변경되면 효과를 다시 실행해야 합니다. +우리의 effect는 message 상태에 "의존"합니다. 따라서 그 상태가 변경되면 effect를 다시 실행해야 합니다. 이제 메시지가 변경될 때 메시지를 로컬 저장소에 저장해 보겠습니다. 이렇게 하면 메시지가 저장되지 않았을 때 신속하게 초안으로 복구할 수 있습니다. 또한 수신자의 사용자 ID를 나타내는 uid prop도 사용하겠습니다. @@ -64,7 +64,7 @@ linter는 이제 uid가 side effect의 일부이기 때문에 의존성 배열 자, 괜찮습니다. 비록 그것이 우리의 컴포넌트 상태는 아니지만, 다른 곳(예: 부모 컴포넌트)의 상태이므로 동일한 개념입니다. -그렇다면 함수 `saveToLocalStorage`는 어떻게 할까요? 효과에서 이를 사용하고 있으니 의존성 배열에 추가해야 할까요? +그렇다면 함수 `saveToLocalStorage`는 어떻게 할까요? effect에서 이를 사용하고 있으니 의존성 배열에 추가해야 할까요? 이 경우에는 아닙니다. 왜 그런지 논의하기 전에, `saveToLocalStorage`가 prop으로 전달되는 아래의 리팩터링된 코드와 비교해 봅시다. @@ -88,7 +88,7 @@ function ComposeMessage({ uid, defaultMessage, saveToLocalStorage }) { 이제 linter가 saveToLocalStorage를 의존성 배열에 추가하라고 요청합니다. 차이점은 무엇일까요? -궁극적으로, React는 effects 내의 상태가 변경될 경우 effect를 re-run 해야 합니다. 이전에 `saveToLocalStorage`가 import 되었을 때, linter는 그 함수가 컴포넌트 상태를 "close over"하여 변경되었을 때 effect를 다시 실행할 필요가 없다는 것을 알고 있습니다. 그러나 `saveToLocalStorage`가 prop일 때, 린터는 부모 컴포넌트가 `ComposeMessage`를 어떻게 구현할지에 대해 충분히 알지 못합니다. 다시 말해, linter는 전체 앱을 탐색하여 `ComposeMessage`가 사용되는 모든 위치와 부모가 prop을 전달하는 방식을 알지 못합니다. 그리고 설령 그렇게 한다 하더라도, linter는 당신이 미래에 그것을 어떻게 사용할지 의도를 알지 못합니다. 이러한 불확실성 때문에, 린터는 이제 `saveToLocalStorage`를 의존성 배열에 추가하라고 요청합니다. +궁극적으로, React는 effects 내의 상태가 변경될 경우 effect를 "re-run" 해야 합니다. 이전에 `saveToLocalStorage`가 import 되었을 때, linter는 그 함수가 컴포넌트 상태를 "close over"하여 변경되었을 때 effect를 다시 실행할 필요가 없다는 것을 알고 있습니다. 그러나 `saveToLocalStorage`가 prop일 때, 린터는 부모 컴포넌트가 `ComposeMessage`를 어떻게 구현할지에 대해 충분히 알지 못합니다. 다시 말해, linter는 전체 앱을 탐색하여 `ComposeMessage`가 사용되는 모든 위치와 부모가 prop을 전달하는 방식을 알지 못합니다. 그리고 설령 그렇게 한다 하더라도, linter는 당신이 미래에 그것을 어떻게 사용할지 의도를 알지 못합니다. 이러한 불확실성 때문에, 린터는 이제 `saveToLocalStorage`를 의존성 배열에 추가하라고 요청합니다. 다음은 부모 컴포넌트가 구현될 수 있는 한 가지 예입니다: @@ -148,7 +148,7 @@ function ComposeMessage({ defaultMessage, saveToLocalStorage }) { 부모 컴포넌트가 어떤 이유로든 다시 렌더링되면(예: 상태 변경 또는 새로운 props), 화살표 함수는 다시 렌더링될 때마다 새로 생성됩니다. 이는 부모가 다시 렌더링될 때마다 `saveToLocalStorage` prop의 함수 아이덴티티가 변경된다는 것을 의미합니다. `ComposeMessage`에서는 `saveToLocalStorage`를 의존성 배열에 포함시켜 버그를 방지해야 하지만, 부모가 다시 렌더링될 때마다 그 아이덴티티가 변경되므로 불필요하게 로컬 저장소에 저장하게 됩니다. 이를 [시연하는 예제](https://codesandbox.io/s/funny-taussig-5tont)를 보세요. -로컬 저장소에 너무 자주 저장하는 것은 괜찮을 수 있지만, 부수 효과가 네트워크 요청인 경우에는 이를 피하고 싶을 것입니다. 따라서 함수의 아이덴티티가 변하지 않도록 유지할 필요가 있습니다. +로컬 저장소에 너무 자주 저장하는 것은 괜찮을 수 있지만, side effect가 네트워크 요청인 경우에는 이를 피하고 싶을 것입니다. 따라서 함수의 아이덴티티가 변하지 않도록 유지할 필요가 있습니다. ### `useCallback` 사용하기