미래 지향적인 웹 앱 구축: The Codest의 전문가 팀이 제공하는 인사이트
The Codest가 최첨단 기술로 확장 가능한 대화형 웹 애플리케이션을 제작하고 모든 플랫폼에서 원활한 사용자 경험을 제공하는 데 탁월한 성능을 발휘하는 방법을 알아보세요. Adobe의 전문성이 어떻게 디지털 혁신과 비즈니스를 촉진하는지 알아보세요...
많은 인터뷰를 진행하면서 숙련된 프로그래머조차도 고급 기능은 말할 것도 없고 Hook을 구별하는 데 어려움을 겪는다는 것을 알게 되었습니다. 그래서 이 글에서는 Hook을 어떻게 사용해야 하는지 설명해 보려고 합니다.
Hook에 대해 기억해야 할 가장 중요한 사항입니다:
사용
;"react"에서 { useState,useEffect }를 가져옵니다;
export default 함수 FunctionComponent() {
const [value, setValue] = useState(1);
const [doubleValue, setDoubleValue] = useState(1);
if (value > 3) {
useEffect(() => setDoubleValue(value * 2),[value]);
}
반환 (
<>
<p>{`싱글 ${값} 더블 ${doubleValue}`}</p>
<button onclick="{()" > setValue(value + 1)}>확인</button>
</>
);
}
```
처음에는 eslint에서 경고를 받게 됩니다:
<code>srcFunctionComponent.js
11:5줄: React 훅 "useEffect"가 조건부로 호출됩니다. <strong>React 후크</strong> 는 모든 컴포넌트에서 정확히 동일한 순서로 호출되어야 합니다. 렌더링 .eslintreact-hooks/rules-of-hooks
보시다시피, 이것은 단지 에슬린트 경고일 뿐이므로 아래에서 아래에서 명령을 추가하여 비활성화할 수 있습니다. 함수 컴포넌트
/* eslint-disable react-hooks/rules-of-hooks */
를 실행하면 작동하지만 Hook을 실행하는 조건을 충족할 때까지만 작동합니다. 바로 다음에 보게 될 것은 이 오류입니다.
잡히지 않은 오류: 이전 렌더링보다 더 많은 후크를 렌더링했습니다.
React 5
함수 컴포넌트 함수 컴포넌트.js:11
React 12
unstable_runWithPriority scheduler.development.js:468
React 17
js index.js:7
js main.chunk.js:905
웹팩 7
react-dom.development.js:15162
왜 이런 일이 발생할까요? React는 Hook이 호출되는 순서에 의존하기 때문에 React는 확인할 줄에 해당 Hook이 없기 때문에 사용 효과에 대해 무엇을 반환할지 알 수 없습니다.
에슬린트는 강력한 도구이며, 많은 잠재적인 버그와 오류를 발견하는 데 도움이 된다는 점을 기억하세요. 경고를 비활성화하는 것은 위험한 일이며, 경고를 무시하면 앱 충돌이 발생할 수 있는지 항상 확인하세요.
어떻게 생겼는지 아시겠죠 😉
const [value, setValue] = useState(0);
따라서 상태(반응 값), 업데이트 함수(세터), 실제 훅(함수), 선택적 초기값의 4가지 요소가 있습니다. 왜 배열을 반환할까요? 원하는 대로 재구성할 수 있기 때문입니다.
이제 마지막 요소인 초기 값에 집중하고 싶습니다. 초기 상태를 전달하는 방법에는 두 가지가 있습니다:
const [value, setValue] = useState(0);
const [value, setValue] = useState(() => {
console.log("INIT");
반환 0;
});
첫 번째 방법이 각 렌더링에서 실제로 호출되는지 확인하는 방법은 무엇인가요? 함수를 생성하고 초기 상태로 전달합니다:
const checkInit = () => {
console.log("INIT");
반환 0;
};
const [value, setValue] = useState(checkInit());
```
이제 두 번째 방법을 사용하여 전달합니다:
const checkInit = () => {
console.log("INIT");
반환 0;
};
const [value, setValue] = useState(() => checkInit());
```
멋지지 않나요?
앱 플로우에서 버그를 만들 수 있는 또 다른 요소: 상태를 업데이트하는 방법을 알고 계시죠?
setValue(1);
그렇군요... 하지만 이전 상태를 기반으로 상태를 업데이트하려면 어떻게 해야 하나요?
setValue(value + 1);
네... 하지만 아니요... 세터 함수를 두 번 연속으로 호출하려고 하면 어떻게 될까요? 이전 상태를 기반으로 상태를 업데이트하는 권장 방법은 함수를 사용하는 것입니다. 이 함수는 이전 상태를 참조하도록 보장합니다.
setValue((prevState) => prevState + 1);
// 객체와 함께:
setUser((prevState) => ({ ...prevState, lastName: "Brzeczyszczykiewicz" }));
이 Hook은 두 개의 인수를 받으며(두 번째 인수는 선택 사항), 부작용을 처리하는 데 사용합니다. 그리고 두 번째 인자로 무엇을 전달하느냐에 따라 Hook은 다르게 호출됩니다:
useEffect(() => {
doSomething();
});
useEffect(() => {
doSomething();
}, []);
useEffect(() => {
doSomething(value);
}, [value]);
사용효과를 사용하면 정리라는 것을 사용할 수 있습니다. 어떤 용도로 사용하나요? 매우 유용하지만 이벤트 리스너를 정리할 때 가장 유용하다고 생각합니다. 어떤 상태에 따라 달라지는 이벤트 리스너를 만들고 싶다고 가정해 봅시다. 상태가 변경될 때마다 새 이벤트 리스너를 추가하고 싶지 않을 것입니다. 몇 번의 렌더링 후에는 리스너가 너무 많아져 앱의 성능에 영향을 미칠 수 있기 때문입니다. 이를 방지하는 가장 좋은 방법은 정리 기능을 사용하는 것입니다. 어떻게 하나요? 사용 효과에 반환 함수를 추가하기만 하면 됩니다.
useEffect(() => {
console.log("부작용 1", count);
return () => {
console.log("DESTROYED 1");
};
});
useEffect(() => {
console.log("side effect 2", count);
return () => {
console.log("DESTROYED 2");
};
}, []);
useEffect(() => {
console.log("side effect 3", count);
return () => {
console.log("DESTROYED 3");
};
}, [count]);
```
사용 효과 훅 안에 있기 때문에, 반환은 의존성 배열에 따라 호출됩니다 - 각 렌더링에서, 첫 번째 렌더링에서만, 또는 의존성 배열의 값이 변경될 때만. 그러나 컴포넌트가 마운트 해제되면 어떤 경우에도 두 번째 인자에 대해 정리가 호출됩니다. 반환값 코드 가 Hook의 실제 코드보다 먼저 호출됩니다. 처음에는 이전 코드를 정리한 다음 새 코드를 생성하는 매우 논리적입니다. 그렇죠?
useEffect(() => {
// 추가 이벤트 리스너
console.log("추가");
return () => {
// removeEventListener
console.log("Remove");
};
}, [value]);
따라서 먼저 제거
메시지를 입력한 다음 추가
.
사용효과와 그 안에 있는 비동기 코드를 사용할 때 주의해야 할 점이 한 가지 있습니다. 아래 코드를 살펴보세요:
useEffect(() => {
fetch("https://picsum.photos/5000/5000").then(() => {
setValue((prevState) => prevState + 1);
});
}, []);
처음에는 괜찮아 보입니다. 데이터를 가져오는 중이고 데이터가 도착하면 상태를 업데이트합니다. 그런데 여기에 함정이 있습니다:
때때로 이러한 경고를 받게 됩니다:마운트되지 않은 컴포넌트에 대해 React 상태 업데이트를 수행할 수 없습니다. 이 문제는 발생하지 않지만 애플리케이션에 메모리 누수가 있음을 나타냅니다. 이 문제를 해결하려면 사용 효과 정리 함수에서 모든 구독 및 비동기 작업을 취소하세요.
그 이유는 그 동안 컴포넌트를 마운트 해제할 수 있지만 약속이 이행된 후에도 앱이 해당 컴포넌트의 상태를 업데이트하려고 시도하기 때문입니다. 어떻게 처리할까요? 컴포넌트가 존재하는지 확인해야 합니다.
useEffect(() => {
let mounted = true;
fetch("https://picsum.photos/5000/5000").then(() => {
if (mounted) {
setValue((prevState) => prevState + 1);
}
});
return () => {
mounted = false;
};
}, []);
```
참고: 컴포넌트를 렌더링한 후 돔이 시각적으로 업데이트되기 전에 콜백이 실행되는 매우 유사한 Hook => useLayoutEffect()가 있습니다. getBoundingClientRect()로 작업할 때 유용하지만 기본적으로 useEffect를 사용해야 합니다. 왜 그럴까요? 이펙트 Hook 안에 복잡한 코드가 있을 때 시각적 업데이트를 차단할 수 있기 때문입니다.
어떤 용도로 사용하나요? 소품을 전달하지 않고 데이터를 공유합니다. 다음 요소로 구성됩니다:
const user = {
이름: "Adam",
lastName: "Kowalski",
};
export const UserContext = createContext(user);
;
```
자식에서는 컨텍스트를 가져와서 사용 컨텍스트 훅을 호출하고 해당 컨텍스트를 인수로 전달해야 합니다.
"./App"에서 { UserContext }를 가져옵니다;
const { name } = useContext(UserContext);
반환 <h1>안녕하세요 {이름}<>
```
짜잔. 멋지네요. 주로 테마 등과 같은 전역 데이터를 전달할 때 사용합니다. 매우 동적으로 변경되는 작업에는 사용하지 않는 것이 좋습니다.
물론 사용자 정의 컨텍스트 제공자와 사용자 정의 Hook을 생성하여 상용구를 줄일 수 있지만... 다음 글에서 사용자 정의 Hook에 대해 다루겠습니다.
state를 관리하고 상태가 변경되면 다시 렌더링할 수 있습니다(예: useState). 리덕스 리듀서와 비슷합니다. 상태 로직이 더 복잡할 때는 사용State보다 낫습니다.
const [state, dispatch] = useReducer(reducer, initialArg);
또한 useReducer에 전달할 수 있는 세 번째 인수가 있는데, 바로 init 함수입니다.
const [state, dispatch] = useReducer(reducer, initialArg, init);
어떤 용도로 사용하나요? 상태를 초기값으로 재설정하고 싶을 때 사용할 수 있습니다. 아래에서 좋은 예를 찾을 수 있습니다:
// 부모
// 자식
함수 init(initialNumber) {
반환 { number: initialNumber };
}
함수 reducer(state, action) {
switch (action.type) {
케이스 "변경":
반환 { 숫자: Math.random() };
case "reset":
return init(action.payload);
default:
새로운 Error();
}
}
export default function ChildComponent({ getFactorial }) {
const [state, dispatch] = useReducer(reducer, initialNumber, init);
return (
<>
<h2>번호: {state.number}</h2>
<button
onclick="{()" > dispatch({ type: "reset", payload: initialNumber })}
>
Reset
</button>
<button onclick="{()" > dispatch({ type: "change" })}>Draw</button>
</>
);
}
언제 사용하나요? 참조 동등성을 달성하고자 할 때(따라서 생성되는 함수 수를 줄이려는 경우). 이 Hook은 값을 반환하는 useMemo와 달리 함수를 반환합니다.
예시: 부모 컴포넌트에서 함수를 생성한 다음 프로퍼티를 통해 전달하기
// 부모
const getSquaredValue = () => count * count;
...
반환 (
>
)
그런 다음 종속성 배열에 해당 함수를 추가한 후 이펙트 Hook이 호출되는 횟수를 하위 컴포넌트에서 확인합니다:
// 자식
useEffect(() => {
console.log("getSquaredValue", getSquaredValue());
}, [getSquaredValue]);
렌더링할 때마다 콘솔에 로그됩니다! 내부의 값이 getSquaredValue()
함수는 변경되지 않았습니다. 하지만 해당 함수를 사용콜백으로 래핑하면 이를 방지할 수 있습니다.
const getSquaredValue = useCallback(() => count * count, [count]))
이 함수에 몇 가지 매개 변수를 전달할 수도 있습니다:
const getSquaredValue = useCallback(
(승수) => 카운트 * 카운트 * 승수,
[count]]
);
const memoizedValue = useMemo(() => {
return doSomething(value);
}, [value]);
이 기능은 두 가지 시나리오에서만 사용해야 합니다:
두 번째 경우를 좀 더 자세히 살펴봅시다. 객체를 종속성으로 사용Effect를 사용하려고 합니다. 객체는 참조로 비교되기 때문에 렌더링할 때마다 useEffect가 호출됩니다. 이러한 문제를 방지하기 위해 useEffect를 useMemo와 결합하여 해당 객체를 메모한 다음 메모된 객체를 종속성 배열에 전달할 수 있습니다. 간단한 예시입니다:
먼저 사용메모 없이 해보세요:
const 호빗 = { 이름: "빌보" };
useEffect(() => {
console.log("Hello ", hobbit.name);
}, [hobbit]);
```
또한 경고를 받게 됩니다:
'hobbit' 객체는 렌더링할 때마다 사용효과 훅(49줄)의 종속성을 변경합니다. 사용효과 콜백 내부로 이동하세요. 또는 'hobbit'의 초기화를 자체 사용 메모 () Hook.eslintreact-hooks/exhaustive-deps에 래핑합니다.
그런 다음 사용메모로 시도해 보세요:
const hobbit = useMemo(() => {
반환 { 이름: "빌보" };
}, []);
useEffect(() => {
console.log("Hello ", hobbit.name);
}, [hobbit]);
```
가장 중요한 것은 useRef는 렌더링 주기에 연결되지 않기 때문에 재렌더링을 트리거하지 않으며, 렌더링 간에 동일한 참조를 유지한다는 점입니다.
const ref = useRef(0);
저장된 값을 호출하려면 현재 프로퍼티(참조는 객체)를 사용해야 합니다. ref.current
이 Hook을 사용할 수 있는 두 번째 경우는 HTML 내부의 요소를 참조하는 경우입니다. 각 요소에는 ref 속성이 있습니다. 따라서 포커스, 이벤트 등을 처리할 수 있습니다.
세 번째 경우는 제어되지 않는 컴포넌트를 처리하기 위해 참조를 사용할 수 있는 경우입니다. 이에 대한 자세한 내용은 리액트 문서,
간단히 설명하면 다음과 같습니다:
내보내기 기본 함수 UncontrolledForm() {
const input = useRef();
const handleSubmit = (e) => {
e.preventDefault();
console.log(input.current.value);
};
반환 (
>
제출
);
}
보시다시피 이벤트 핸들러는 없으며 입력된 값만 기억합니다. 제출할 때처럼 필요할 때 저장된 값을 읽어야 할 때 기본적인 양식을 처리하는 데 유용합니다.
보너스: 이전 상태 값을 기억해야 할 때 유용합니다. state를 참조에 전달하기만 하면 되는 useEffect Hook을 사용할 수 있습니다.
const [value, setValue] = useState("");
let prevValue = useRef("");
useEffect(() => {
prevValue.current = value;
}, [value]);
setValue(e.target.value)}>;
보시다시피 Hook은 그렇게 분명하지 않습니다. 우리는 그것들을 결합하여 많은 문제를 해결할 수 있습니다. 이 주제를 공부하면 분명 큰 도움이 될 것입니다.
그리고 사용자 지정 후크도 있습니다...
결론적으로 React 후크 방식에 혁명을 일으켰습니다. React 개발자 접근 빌딩 웹 애플리케이션 . 함수형 컴포넌트에서 상태 및 수명 주기를 보다 직관적이고 효율적으로 관리할 수 있는 방법을 제공함으로써 후크는 React 개발 .
숙련된 개발자이든 React를 막 시작한 개발자이든 가장 많이 사용되는 훅과 그 사용 사례를 이해하는 것은 매우 중요합니다. 사용 상태, 사용 효과, 사용 컨텍스트 등의 후크가 대표적입니다, React 구성 요소 보다 깔끔하고 재사용 가능한 코드로 빌드할 수 있습니다. 또한, 다음과 같은 기능을 만들 수 있습니다. 사용자 지정 후크 를 사용하면 개발자가 여러 컴포넌트에서 로직을 캡슐화하고 공유하여 코드 재사용성과 모듈성을 촉진할 수 있습니다. React가 계속 발전하고 새로운 기능을 도입함에 따라 후크는 프레임워크의 잠재력을 최대한 활용하는 데 있어 중심적인 역할을 할 것입니다.
따라서 작은 기능의 앱이든 대규모 웹 애플리케이션이든 관계없이 React 후크 는 개발 워크플로우를 개선하고 강력하고 풍부한 기능을 개발할 수 있는 수많은 가능성을 열어줍니다. React 애플리케이션 .
1부 끝
자세히 읽어보세요:
JavaScript는 완전히 죽었습니다. 인터넷의 어떤 남자