일상적인 횡설수설에 반응: 캡슐화된 목록 항목
13734 단어 reactjavascript
할 일 앱을 만들고 싶다고 가정해 보겠습니다. 또는 목록이 있는 다른 항목.
목록이 있으므로 작업 목록 데이터는 상위 구성 요소에 저장된 상태가 된 다음 하위 구성 요소로 확산됩니다. 이 같은:
function Root() {
const [tasks, setTasks] = useState([INITIAL_TASK])
return <main>
<h1>my to do</h1>
<ul>
{tasks.map(task => (<TaskView value={task} setValue={...}/>))}
</ul>
</main>
}
여기에는 두 가지 주요 목표가 있습니다.
<TaskView />
는 적절하게 캡슐화되어야 합니다. 응용 프로그램에 있는 WHERE에 대해 신경쓰지 않아야 합니다. 따라서 배열의 인덱스에 대해 알면 안 됩니다. <TaskView />
가 memo()
로 래핑됩니다. memo()
가 작동하려면 기본 데이터가 변경되지 않은 경우 소품이 변경되지 않도록 해야 합니다. 접근법 1: Setter 콜백
우리는 다음과 같이
TaskView
를 씁니다.(PS: 이 문서의 코드는 테스트되지 않았거나 보풀이 없습니다)
const TaskView = memo((
{ value, setValue }:
{ value: Task, setValue: (cb: (arg: (old: Task) => Task) => void }
) => {
const handleChangeName = useCallback((event) => {
const newName = event.target.value
setValue(old => ({ ...old, name: newName }))
}, [setValue])
return ...
})
이것은 적절하게 캡슐화되지만 소비자를 작성할 때 몇 가지 문제가 발생합니다.
function Root() {
const [tasks, setTasks] = useState([INITIAL_TASK])
const setTaskAtIndex = useCallback((value: Task, index: number) => {
setTasks(previous => {
// ...
})
}, [])
return <main>
<h1>my to do</h1>
<ul>
{tasks.map((task, idx) => {
const setValue = callback => {
const newValue = callback(task)
setTaskAtIndex(newValue, idx)
}
return <TaskView value={task} setValue={setValue}/>
})}
</ul>
</main>
}
따라서 여기서 문제는
setValue
가 모든 렌더링에서 항상 새로운 참조를 갖게 되어 memo()
를 쓸모 없게 "렌더링"한다는 것입니다. 동적 크기의 루프 내부에 있기 때문에 적용할 수 없습니다useCallback
.순진한 접근 방식은
index
에 추가 소품 TaskView
을 추가하는 것이지만 캡슐화가 깨지기 때문에 해킹이 될 수 있습니다.useCallback
를 사용할 수 있도록 "어댑터 구성 요소"를 만들어 이 문제를 해결했습니다. 이제TaskView
는 데이터가 변경될 때만 다시 렌더링해야 합니다.function TaskViewAdapter(props: {
value: Task,
setValueAtIndex: (value: Task, index: number) => void ,
index: number
}) {
const setValue = useCallback((callback) => {
const newValue = callback(value)
setValueAtIndex(newValue, index)
}, [value, setValueAtIndex, index])
return <TaskView value={props.value} setValue={setValue} />
}
HTML 이벤트와 다른 점은 무엇입니까?
목록을 처리하는 오래되고 일반적인 접근 방식은 데이터 태그(또는 다른 속성)를 사용하는 것입니다. 이 접근 방식을 사용하면 중간 구성 요소의 도움 없이 효율적인 렌더링에 도달할 수 있습니다.
function Main() {
const handleClick = useCallback((ev) => {
console.log('index', ev.target.dataset.index)
}, [])
return <ul>
<li><button data-index="1" onClick={handleClick}>Button 1</button></li>
<li><button data-index="2" onClick={handleClick}>Button 2</button></li>
</ul>
}
이것은 데이터가 HTML 이벤트에서 방출되기 때문에 작동합니다.
여기서 변경된 사항은 무엇입니까?
setValue
콜백과 달리 HTML 이벤트는 데이터와 함께 컨텍스트를 가져옵니다. 단순히 값이 아닌 전체 요소를 가져옵니다.이것은 부모가 요소에 데이터를 첨부하고 이벤트를 처리할 때 해당 데이터를 다시 읽을 수 있음을 의미합니다. 그리고
<button>
의 내부 구현은 여전히 부모가 첨부된 추가 정보가 무엇인지 알 필요가 없습니다.단순히 데이터를 방출하는 대신 구성 요소에 대한 추가 컨텍스트 데이터가 있는 이벤트 같은 것을 방출하여 복제를 시도할 수 있습니다. 사용자 정의 이벤트 방출은 React "표준"에 포함되지 않기 때문에 특정 프로젝트에 대한 표준 이벤트 형식을 정확히 지정해야 합니다.
const event = createEvent({
component: getSelfRef(),
data,
})
onChange(event)
또한 (후크 구성 요소를 사용하는 경우) 래퍼 "어댑터"구성 요소를 만들지 않고는 현재 구성 요소 참조를 가져올 수 있는 방법이 없습니다. 결국 우리는 어댑터가 필요한 동일한 경우에 다시 빠지게 됩니다.
Reference
이 문제에 관하여(일상적인 횡설수설에 반응: 캡슐화된 목록 항목), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/wkrueger/react-daily-ramblings-encapsulated-list-items-5e38텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)