import styled from '@emotion/styled';
import useNestedHook from '@Hook/nested-custom-hook/useNestedHook';
import { memo } from 'react';
import { v4 as uuidv4 } from 'uuid';
export default function NestedCustomHook() {
const formId = `user_${uuidv4()}`;
const { formValues, setter } = useNestedHook();
return (
<VerticalFlexContainer>
<form>
<label htmlFor={`name_${formId}`}>
Name:
<input
id={`name_${formId}`}
name="name"
type="text"
value={formValues.name}
onChange={(e) => {
setter.setName(e.target.value);
}}
/>
</label>
<label htmlFor={`email_${formId}`}>
Email:
<input
id={`email_${formId}`}
name="email"
type="text"
value={formValues.email}
onChange={(e) => {
setter.setEmail(e.target.value);
}}
/>
</label>
</form>
<HorizontalFlexContainer gap="20px">
<MemoizedSpan label="Name" data={formValues.name} />
<MemoizedSpan label="email" data={formValues.email} />
</HorizontalFlexContainer>
</VerticalFlexContainer>
);
}
const MemoizedSpan = memo(
({ label, data }: { label: string; data: string }) => {
console.log('MemoizedSpan', label, data);
return (
<CustomSpan>
{label}: {data}
</CustomSpan>
);
}
);
const HorizontalFlexContainer = styled.div<{ gap: string }>`
display: flex;
gap: ${(props) => props.gap};
`;
const VerticalFlexContainer = styled.div`
display: flex;
flex-direction: column;
gap: 1rem;
padding: 12px;
`;
const CustomSpan = styled.span`
display: inline-block;
background-color: 'red';
`;
MemoizedSpan
이라는 컴포넌트를 임의로 선언해 보았다(emotion/styled는 무시하자). 공식문서대로라면, prop이 바뀌지 않는 부분의 컴포넌트는 리렌더링이 되지 않아야 한다.
실제로 잘 동작한다. Name 인풋을 수정할 때 email 쪽의 Span컴포넌트는 리렌더링 되지 않는다.
물론 memo를 사용하더라도 memo된 컴포넌트의 내부 state가 변경되거나 사용 중인 context가 변경되면 리렌더링 된다는 점에는 주의해야 한다.
객체를 props로 넘기는 경우는 컴포넌트를 작성하면서 자주 접하는 상황이다.
memo는 내부적으로 Object.is
로 비교를 하여 prop이 이전 렌더링 때의 prop과 동일한 지를 비교한다.
그러면 Object.is
는 어떻게 동작을 할까?
내가 궁금한 점은 중첩객체일 때 과연 Object.is가 제대로 동작하느냐였다. 실무에서 prop에 중첩객체를 전달해야 하는 경우가 잦았는데 이 경우에도 제대로 객체 간 비교를 수행하는지 궁금했다.
console.log(Object.is({ name: 'child1' }, { name: 'child1' }));
//false