single Page Application 단점은 컴포넌트간 state 공유가 어렵습니다.
부모 > 자식 컴포넌트 관계만 props로 전송이 가능합니다.
부모 > 자식 > 손자 부모 컴포넌트에서 손자 컴포넌트에 props를 전달하려면, 자식 컴포넌트에게 props를 전송하고, 다시 자식 컴포넌트가 손자 컴포넌트에 전송해야하기 때문에 번거롭고 가독성을 망치며 복잡해집니다.
이를 해결하기 위한 Contenxt API를 사용해서 props 전송 없이 state를 공유합니다.
(App.js)
export let invenCT = React.createContext();
function App(){
let [inven, setInven] = useState([10,11,12]);
(생략)
}
(App.js)
export let invenCT = React.createContext();
function App(){
let [inven, setInven] = useState([10,11,12]);
return (
<invenCT.Provider value={ {inven} }>
<Detail shoes={shoes}/>
</invenCT.Provider>
)
}
(Detail.js)
import {useContext} from 'react';
import {invenCT} from './App.js';
function Detail(){
let {inven} = useContext(invenCT)
return (
<div>{inven}</div>
)
}
- state 변경시 재렌더링이 필요없는 요소까지 렌더링한다.
- 다른 페이지에서 컴포넌트 재사용이 어렵다.
이를 해결하기 위해 외부 라이브러리인Redux를 사용한다
- 애니메이션 동작 전 스타일을 담을 className 만들기
- 애니메이션 동작 후 스타일을 담을 className 만들기
- transition 속성도 추가
- 원할 때 2번 탈부착
.start {
opacity : 0
}
.end {
opacity : 1;
transition : opacity 0.5s;
}
function TabContent({tab}){
let [fade, setFade] = useState('')
useEffect(()=>{
setFade('end') // 부착
return () => {
setFade('') // 떼었다가
} // 떼었다가 부착하기 위해 clean up function 사용
}, [tab]) // tab state가 변경될 때 마다 해당 코드 실행(클래스명 변경)
return (
<div className={`'start {fade}`}> // className은 띄어쓰기를 해야 중첩시킬 수 있기 때문에 주의!
{ [<div>내용0</div>, <div>내용1</div>, <div>내용2</div>][tab] }
</div>
)
}
리액트 18버전 이상부터는 automatic batch 라는 기능이 생겼습니다.
automatic batch란 state 변경함수들이 연달아서 여러개 처리되어야한다면 state 변경함수를 다 처리하고 마지막에 한 번만 재렌더링됩니다.
그래서 'end' 로 변경하는거랑 ' ' 이걸로 변경하는거랑 약간 시간차사용했습니다.
setTimeout?()말고 flushSync()도 사용 가능합니다.
flushSync()는 비동기적인 코드를 강제로 동기적으로 만들고 react에서 리렌더링을 강제하도록 하여 automatic batching을 막아줍니다.
function TabContent({tab}){
let [fade, setFade] = useState('')
useEffect(()=>{
setTimeout(() => {setFade('end')}, 100) // 부착
return () => {
setFade('') // 떼었다가
}
}, [tab])
return (
<div className={`'start {fade}`}>
{ [<div>내용0</div>, <div>내용1</div>, <div>내용2</div>][tab] }
</div>
)
}