코딩 애플 React Part 2 - (2)

신승준·2022년 7월 11일
0

styled-components를 쓰면 css 파일이 없어도 된다.

styled-components의 장점

  1. css를 JS 파일에서 전부 해결한다.
  • npm install styled-components로 설치
  • styled-components를 사용할 곳에 다음과 같이 코드를 적는다.

App.js

import styled from 'styled-components';

Detail.js

// 스타일이 입혀진 button이 된 것이다.
let YellowBtn = styled.button`
    background: yellow;
    color: black;
    padding: 10px;
`

function Detail(props) {
    // 이 때 id로는 string 형태가 들어온다.
    let {id} = useParams();
    let detail = props.shoes.find(x => x.id === Number(id));

    return (
        <div className="container">
            <YellowBtn>버튼</YellowBtn>

  1. styled-components로 생성된 컴포넌트는 스타일이 다른 js파일을 오염시키지 않는다.
  • react는 html 파일들을 하나의 html 파일로, js파일들을 하나의 js파일로, css파일들을 하나의 css파일로 합친다.
    • 따라서 App.css에 적혀진 css style들이 Detail.js에도 적용이 된다.
    • 큰 웹/앱이 되면 간섭할 가능성이 크다.
    • App.module.css라고 파일을 작명하면, App.js에만 적용이 되도록 만들 수도 있긴 하다.
    • 마찬가지로 Detail.js에만 적용이 되는 css파일을 만들려면 Detail.module.css를 만들면 된다.
  1. 페이지 로딩시간을 단축시킨다.
  • styled로 작성된 컴포넌트는 css파일로 압축되는 것이 아니라, HTML 파일에서 <style>이라는 태그로 만들어져서 HTML 파일에 들어가게 된다.
    • CSS 파일이 따로 필요가 없어져서, 페이지 로딩시간이 향상된다.

  • props로 styled-components를 재활용 할 수 있다.
    • 밑의 뜻은, YellowBtn 컴포넌트를 가져다 쓸 때, bg라는 props의 인자를 background에 가져다 쓸 수 있다는 의미이다.
    • YellowBtn 컴포넌트에서 bg="blue"를 전달하고 있다.
let YellowBtn = styled.button`
    background: ${props => props.bg};

...

<YellowBtn bg="blue">버튼</YellowBtn>

styled 컴포넌트에서는 프로그래밍이 가능해진다.

  1. 간단한 프로그래밍이 가능하다.
  2. 복사가 가능하다.
// 스타일이 입혀진 button이 된 것이다.
let YellowBtn = styled.button`
    background: ${props => props.bg};
    color: ${props => props.bg == 'blue' ? 'white' : 'black'};
    padding: 10px;
`

...

// 복사해서 커스터마이징이 가능하다.
let NewBtm = styled.button(YellowBtn)`
    // 커스터마이징 가능
`



Lifecycle과 useEffect 1

  • Lifecylce : 컴포넌트도 사람처럼 태어나고 죽는다고 본다.
  • 지금 Detail 컴포넌트는 detail/0~2 페이지로 들어가면 보이게 된다.
    • 이렇게 페이지에 보이게 되는 것을, 컴포넌트가 mount된다 혹은 장착된다라고 표현한다. - mount
    • 가끔 수정되고 하며(재렌더링) - update
    • 필요 없으면 제거되기도 한다. - unmount
  • 이렇게 흘러가는 것을 lifecycle이라고 하고, 그 사이사이에 간섭이 가능하다.
    • 즉 사이사이에 코드 실행이 가능하다.
    • 해당 강의에서는 갈고리(훅, Hook)를 건다고 표현한다.
    • mount에다가 갈고리를 걸면 mount될 때 해당 갈고리의 코드를 실행해준다고 보면 된다.

옛날 방식(component)

// 옛날 방식
class Detail2 extends React.Component {
    componentDidMount() {
    // mount 될 때 해당 코드가 실행된다.  
    }
    
    componentDidUpdate() {
    // update 될 때 해당 코드가 실행된다.  
    }
    
    componentWillUnmount() {
    // unmount 될 때 해당 코드가 실행된다.  
    }
}

요즘 방식(function)

  • useEffect라는 훅을 사용한다.
    • 해당 훅 안에 쓰인 코드는 컴포넌트가 mount 혹은 update를 할 때 실행된다.
  • 해당 훅에 console.log로 아무거나 찍어보면 2번 실행되는 것을 알 수 있는데, 이는 react 상에서 디버깅을 위해 일부러 해주는 것이다.
    • 없애고 싶으면 index.html에서 <React.StrictMode> 태그를 없애주면 된다.
function Detail(props) {
    useEffect(() => {
        // mount, update될 때 여기의 코드가 실행된다.
        console.log('hi');
    })
  
...

useEffect를 사용하는 이유

  • useEffect 안의 코드는, 함수의 return문에 있는 HTML 렌더링 다음에 실행된다.
    • 조금 더 효율적이라고 볼 수 있다.
    • HTML을 조금 더 빠르게 보여줘서 사용자에게 빠른 느낌을 줄 수 있다.
      • HTML을 먼저 보여주고 useEffect의 코드가 실행된다.
      • 따라서 엄청 긴 시간이 필요한 for문을 사용할 때는, 이 for문을 useEffect에 넣어줘야, 사용자에게 빠르게 HTML을 보여줄 수 있다.
function Detail(props) {
    useEffect(() => {
        // mount, update될 때 여기의 코드가 실행된다.
        
    })
    
    for (let i = 0; i < 50000; i++) {
        console.log(1);
    }

위와 같이 할 경우 for문이 오래 걸려 HTML을 늦게 보여줄 수 있다. 밑과 같이 고쳐주면 좋다.

function Detail(props) {
    useEffect(() => {
        // mount, update될 때 여기의 코드가 실행된다.
        for (let i = 0; i < 50000; i++) {
            console.log(1);
        }
    })

  • useEffect를 사용하는 경우
    • 어려운 연산, 긴 연산들이 함수 안에 존재해야 할 때
    • 서버에서 데이터를 가져오는 작업(HTML을 보여주는 것보다 덜 중요하고 오래 걸린다)
    • 타이머를 사용할 때

  • Effect라는 이름이 붙는 이유
    • 프로그래밍할 때 사용하는 용어로 Side Effect라는 용어가 있다.
      • 함수의 핵심기능과 상관 없는 부가기능을 뜻한다.(곁다리 기능들)
      • function 컴포넌트의 주요 기능은 HTML렌더링이다. 이 외를 부가기능이라고 볼 수 있다.

  • 과제
    • 2초 뒤 해당 div 태그 사라지게 만들기
    • setTimeout 이용
function Detail(props) {
    let [discountDisplay, setDiscountDisplay] = useState(true);
    
    useEffect(() => {
        // mount, update될 때 여기의 코드가 실행된다.
        setTimeout(() => {setDiscountDisplay(false)}, 2000);
    })

...

return (
  <div className="container">
  	{discountDisplay === true ? <Discount></Discount> : null}
  <div className="row">

...
  
function Discount() {
    return (
        <div className="alert alert-warning">
                2초 이내 구매 시 할인
        </div>
    );
}



Lifecycle과 useEffect 2

dependency

  • 밑의 코드는 mount, update 시 모두 해당 코드가 실행된다.
useEffect(() => {
    // mount, update될 때 여기의 코드가 실행된다.
    setTimeout(() => {setAlert(false)}, 2000)
  	console.log(1);
})

  • 밑의 코드는 속해있는 컴포넌트가 mount될 시 실행된다.
    • 지금 상태에서는 update 될 시 밑의 코드는 실행되지 않는다.
    • 즉 mount될 때, 1회만 발생하게 된다.
useEffect(() => {
    // mount, update될 때 여기의 코드가 실행된다.
    setTimeout(() => {setAlert(false)}, 2000)
  	console.log(1);
}, [])

useEffect(() => {
    // mount, update될 때 여기의 코드가 실행된다.
    setTimeout(() => {setAlert(false)}, 2000)
  	console.log(1);
}, [count])
  • 위와 같이 하면, count라는 state가 변경될 때 useEffect 안의 코드가 실행된다.
    • 물론 mount 시에도 실행된다.
    • 그리고 count라는 state가 변경될 때에도 위의 코드가 실행되어 console.log(1)이 계속 찍히는 것을 볼 수 있다.
      • 즉 count라는 state가 변경되어 해당 컴포넌트의 update가 발생할 때 useEffect 안의 코드가 실행되는 것이다.
      • dependency라고 일컫는 [] 안에는 어떤 update가 일어나면 useEffect안의 코드가 실행될 지 정하는 곳이다.

return

  • 다음과 같이 useEffect에서는 return문 작성이 가능하다.
    • useEffect가 실행되기 전에, 해당 return문의 코드가 실행된다.
    • return문 안의 function을 clean up function이라고 한다.
    • setTimeout과 같은 함수를 쓰면 브라우저 안에 timer 객체가 생성된다.
      • mount 혹은 update 될 때마다 이 timer가 계속 생긴다면 많은 메모리를 차지하게 된다.
      • 따라서 기존 timer를 없애주는 작업이 필요하다.
      • 이렇게 하면 쓸데 없는 비효율을 방지할 수 있다.
    • useEffect의 setTimeout 함수가 실행되기 전에, 미리 return문의 clean up function이 기존의 timer를 제거해준다.
    • 참고로 mount, 즉 처음 생겨날 때(장착될 때)에는 return문이 실행되지 않는다.
    • 그러나 unmount 시에는 실행이 된다.
      • unmount는 컴포넌트가 삭제되는 것을 뜻하는데, 다른 페이지로 넘어갔을 때라고 생각하면 된다.
  • setTimeout은 생성한 timer를 식별할 양의 정수를 반환한다.
    • clearTimeout이라는 함수를 통해 해당 timer를 제거할 수 있다.
useEffect(() => {
    // mount, update될 때 여기의 코드가 실행된다.
    // 이렇게 작성하여도, setTimeout 함수는 실행된다. 함수를 실행하는 ()가 붙어 있기 때문이다.
    let timer = setTimeout(() => {setAlert(false)}, 2000)
    
    return () => {
        clearTimeout(timer);
    }
})

  • 서버로 데이터를 요청하는 코드가 useEffect 안에 쓰이기도 하는데, 만약 이 작업이 2초가 걸린다고 가정하자.
    • 이 때 이 2초 사이에 재렌더링이 된다면 서버로 보내는 데이터 요청이 계속 쌓이게 된다.
    • 이는 많은 버그를 불러올 수 있어, 기존의 데이터 요청을 삭제하고 다시 데이터 요청을 하게 해야 한다.

정리

  1. 재렌더링될 때 마다 코드를 실행하고 싶으면 dependency 없이 작성한다.
useEffect(() => {})
  1. mount시 1회 코드 실행하고 싶으면
  • 특정 조건에서, 즉 update 될 때마다 useEffect의 함수가 실행되길 바란다면 dependency에 state 등을 넣어주면 된다.
useEffect(() => {}, [])
  1. 삭제될 때 해당 코드가 1번만 실행되었으면 한다면
useEffect(() => {
    return () => {
        
    }
}, [])
  1. useEffect 실행 전에 어떤 걸 비우고 싶으면 return () => {}를 이용한다.
  2. 특정 state 변경 시, 즉 update 시에 해당 useEffect 코드가 실행되길 바란다면 dependency를 사용한다.



서버 통신을 위한 ajax 1

  • react에서는 거의 ajax를 이용해 서버와 통신한다.

서버에 데이터 요청 시

  • 어떤 데이터인지(URL)
  • 어떤 방법으로 요청할지(GET or POST)
    • GET: 데이터를 가져올 때
    • POST: 데이터를 서버로 보낼 때



ajax

  • GET/POST 시 새로고침 없이 가능하다.
  • ajax 사용을 위한 3가지 방법
    • XMLHttpRequest (옛날)
    • fetch() (요즘)
    • axios (외부 라이브러리)
  • axios
    • 터미널에 npm install axios 입력하여 라이브러리 설치
    • App.js에 다음과 같이 코드 작성
import axios from 'axios'
  • ajax GET 버튼
<Button variant="success" onClick={() => {
	// ajax GET 요청
	// axios.get('url')
	axios.get('https://codingapple1.github.io/shop/data2.json')
	// 요청결과는 axios.get('url').then()
	// data가 ajax GET을 통해 받아온 데이터이다.
	.then((result) => {
		// console.log(result);
		console.log(result.data);
	})
}}>Ajax</Button>
  • ajax 요청이 실패할 경우
    • 인터넷이 안되거나
    • 서버가 꺼졌거나
    • 요청한 URL이 잘못되었거나
<Button variant="success" onClick={() => {
	// ajax GET 요청
	// axios.get('url')
	axios.get('https://codingapple1.github.io/shop/data2.json')
	// 요청결과는 axios.get('url').then()
	// data가 ajax GET을 통해 받아온 데이터이다.
	.then((result) => {
		// console.log(result);
		console.log(result.data);
	})
	// ajax 요청 시 실패할 경우 특정 코드를 실행하고 싶다면 catch 사용
	// 즉 예외 처리
	.catch(() => {
		console.log('실패');
	})
}}>Ajax</Button>
  • 과제
    • 데이터 요청 시(ajax GET) 새로운 상품 목록 생성
<Button variant="success" onClick={() => {
	axios.get('https://codingapple1.github.io/shop/data2.json')
	.then((result) => {
		let newShoes = [...shoes];
		// let newShoes = [...shoes, ...result.data];
		result.data.forEach((element) => {
			newShoes.push(element);
		})
		setShoes(newShoes);
	});
}}>Ajax</Button>

* 배열들을 합칠 때에도 spread operator를 사용할 수 있다.




서버 통신을 위한 ajax 2

POST

  • 서버로 데이터 전송
    • GET도 쓰나 거의 post를 사용한다.
    • 앞에는 url, 뒤에는 보낼 데이터(object)를 적는다.
axios.post('/url을 적으면 된다.', {name : 'kim'})



  • 데이터를 여러 군데에 보내고 싶다면 Promise 사용
    • 밑과 같이 작성 시, 2개의 GET 요청이 성공하면 then의 함수를 실행한다.
// 데이터 요청을 여러 군데에 하고 싶을 때
Promise.all([axios.get('/url1'), axios.get('/url2')])
.then((result) => {
	
})
  • 원래 서버와 데이터를 주고 받을 때는 문자 자료만 가능하다.
    • {name : "kim"}이 아니라, "{"name": "kim"}"으로 받는다.
    • 하지만 출력해보면 배열이 나오곤 한다.
    • 이는 axios 라이브러리가 문자 자료를 array 혹은 object로 변환해주었기 때문이다.
  • fetch
    • 이와 다르게 fetch는 문자 자료 그대로 받아진다. 즉, 자동으로 array 혹은 object로 변환해주지 않는다.
    • 따라서 json()으로 통해 자동 변환해주는 과정이 1번 더 필요하다.
// fetch
fetch('url')
.then(result => result.json())
.then((result) => {
	console.log(result);
})



리액트에서 탭 UI 만들기

<Nav variant="tabs" defaultActiveKey="link0">
    <Nav.Item>
        <Nav.Link eventKey="link0">버튼0</Nav.Link>
    </Nav.Item>
    <Nav.Item>
        <Nav.Link eventKey="link1">버튼1</Nav.Link>
    </Nav.Item>
    <Nav.Item>
        <Nav.Link eventKey="link2">버튼2</Nav.Link>
    </Nav.Item>
</Nav>
  • 여기서 defaultActiveKey를 통해, 처음에는 어떤 버튼이 default로 눌러져 있을지 결정할 수 있다.

  • 삼항 연산자는 다음과 같이 여러 번 써야 되는 경우가 있어서 비효율적일 수 있다.
{ tab === 0 ? <div>내용0</div> : null}
{ tab === 1 ? <div>내용1</div> : null}
{ tab === 2 ? <div>내용2</div> : null}

  • HTML 안에서(return문 안에서) if문은 불가능해서 바깥에 써야 한다.
function TabContent(props) {
    if (props.tab === 0) {
        return <div>내용0</div>
    }
    
    if (props.tab === 1) {
        return <div>내용1</div>
    }
    
    if (props.tab === 2) {
        return <div>내용2</div>
    }
}

  • 결론적으로는, 다음과 같이 작성하면 if문을 HTML(return문)에서 구현이 가능하다.
...

            <Nav variant="tabs" defaultActiveKey="link0">
                <Nav.Item>
                    <Nav.Link eventKey="link0" onClick={() => setTab(0)}>버튼0</Nav.Link>
                </Nav.Item>
                <Nav.Item>
                    <Nav.Link eventKey="link1" onClick={() => setTab(1)}>버튼1</Nav.Link>
                </Nav.Item>
                <Nav.Item>
                    <Nav.Link eventKey="link2" onClick={() => setTab(2)}>버튼2</Nav.Link>
                </Nav.Item>
            </Nav>
            
            {/* 삼항연산자를 여러 개 쓰면 비효율적이다. */}
            {/* { tab === 0 ? <div>내용0</div> : null}
            { tab === 1 ? <div>내용1</div> : null}
            { tab === 2 ? <div>내용2</div> : null} */}
            
            <TabContent tab={tab}></TabContent>
        </div>
    );
}

function TabContent(props) {
    if (props.tab === 0) {
        return <div>내용0</div>
    }
    
    if (props.tab === 1) {
        return <div>내용1</div>
    }
    
    if (props.tab === 2) {
        return <div>내용2</div>
    }
}

...

  • props. 계속 쓰는 것이 귀찮다면
function TabContent({tab}) {
// function TabContent({prop1, prop2 ...})
    if (tab === 0) {
        return <div>내용0</div>
    }
    
    if (tab === 1) {
        return <div>내용1</div>
    }
    
    if (tab === 2) {
        return <div>내용2</div>
    }
}
  • array를 이용해 위의 코드를 단축시킬 수 있다.
// 이렇게 array를 이용하는 것도 가능하다.
function TabContent({tab}) {
    return [<div>내용0</div>, <div>내용1</div>, <div>내용2</div>][tab]
}



멋있게 컴포넌트 전환 애니메이션 주는 방법

- Transition

  • step
    1. 애니메이션 동작 전 className 만들기
    2. 애니메이션 동작 후 className 만들기
    3. className에 transition 속성 추가
    4. 원할 때 2번(애니메이션 동작 후) className 부착
  • fade-in 애니메이션 만들기
    • transition
      • opacity가 0.5초에 걸쳐 변하도록 해달라는 뜻이다.
.start {
  opacity: 0;
}

.end {
  opacity: 1;
  transition: opacity 0.5s;
}

  • 밑과 같은 상태에서 각각의 버튼을 누를 때마다, 즉 각각 태그의 onClick에서 className="end"를 추가하라고 하는 것은 코드적으로 비효율적이다.
<Nav variant="tabs" defaultActiveKey="link0">
    <Nav.Item>
        <Nav.Link eventKey="link0" onClick={() => setTab(0)}>버튼0</Nav.Link>
    </Nav.Item>
    <Nav.Item>
        <Nav.Link eventKey="link1" onClick={() => setTab(1)}>버튼1</Nav.Link>
    </Nav.Item>
    <Nav.Item>
        <Nav.Link eventKey="link2" onClick={() => setTab(2)}>버튼2</Nav.Link>
    </Nav.Item>
</Nav>

...

function TabContent({tab}) {
    return (
        <div className="start">
            {[<div>내용0</div>, <div>내용1</div>, <div>내용2</div>][tab]}
        </div>
    )
}
  • 따라서 function TabContent에서 변경해주어야 한다.
    • tab이 변할 때, 즉 해당 버튼이 눌러졌을 때 className="end"를 추가하면, function TabContent 함수에서 1개의 코드 작성으로 처리가 가능해져 효율적이다.
    • tab이 변할 때, 즉 재렌더링(update)될 때 className이 추가되면 되는 것이므로, useEffect를 사용한다.
    • 하지만 밑과 같이 해두면 제대로 opacity 변화가 일어나지 않는다. end가 안 붙어 있는 상태도 필요한데, mount 되면서 end가 붙은 후, end가 잠시라도 제거되는 코드가 없기 때문이다.
function TabContent({tab}) {
    let [fade, setFade] = useState('');
    
    useEffect(() => {
        setFade('end');
    }, [tab])
    
    return (
        <div className={'start ' + fade}>
        {/* <div className={`start ${fade}`} */}
            {[<div>내용0</div>, <div>내용1</div>, <div>내용2</div>][tab]}
        </div>
    )
}
  • 따라서 다음과 같이 해두면 될 줄 알았으나, 이는 리액트의 automatic batching으로 인해 제대로 동작하지 않는다.
    • react에서는 state 변경 함수를 쓸 때마다 재렌더링을 해주는 것이 아니라, state 변경이 다 되고 나서, 마지막에 한 번에 재렌더링한다.
    • 따라서 다음과 같이 하면, 합쳐서 한 번에 실행 해버리기 때문에 결국엔 end만 남게 된다.
    • 따라서 opacity가 0에서 1로 천천히 변하는 과정이 무시된다.
useEffect(() => {
    // setFade('');
    setFade('end');
    
    return () => {
        setFade('');
    }
}, [tab])
  • setTimeout 함수를 써서, setFade('')와 setFade('end')가 automatic batching이 되지 않도록 한다.
    • 또한 props 대신 다음과 같이 {tab}을 씀으로써 props.tab을 대신할 수 있다.
    • {tab, shoes}와 같이 다수의 것도 대신 가능하다.
function TabContent({tab}) {
    let [fade, setFade] = useState('');
    
    useEffect(() => {
        let timer = setTimeout(() => { setFade('end')}, 10);
        
        return () => {
            clearTimeout(timer);
            setFade('');
        }
    }, [tab])
    
    return (
        <div className={'start ' + fade}>
        {/* <div className={`start ${fade}`} */}
            {[<div>내용0</div>, <div>내용1</div>, <div>내용2</div>][tab]}
        </div>
    )
}



props 대신 Context API

  • Single Page Application의 단점

    • 컴포넌트간 state 공유가 어렵다.
    • 부모 컴포넌트에서 자식 컴포넌트로만 state를 전달할 수 있다.
    • 밑의 사진과 같이 될 경우, 여러 번 state를 넘겨야 하는 것이 귀찮아질 수 있다.
  • 이렇게 여러 번 state를 쓰는 것이 귀찮다면

    • Context API (리액트 기본 문법)
    • Redux (외부 라이브러리)

Context API

  • 실전에서는 잘 쓰지 않는다.
    • 성능 이슈
    • 컴포넌트 재활용이 어려워진다.
  • 따라서 이런 것이 있다라고 생각하고 참고 용도로만 알아두면 된다.

  • Context API를 쓰면 자식 컴포넌트는 props 없이 state 사용이 가능해진다.
    • App의 stocks를 App의 자식인 Detail과, App의 자식의 자식인 tabContent에서 사용해보도록 하겠다.
function App() {
    let [shoes, setShoes] = useState(data);
	let [userClicks, setUserClicks] = useState(1);
	let [loadingDisplay, setLoadingDisplay] = useState(false);
	let [stocks, setStocks] = useState([10, 11, 12]); 
  • Context API 셋팅

App.js

  1. createContext()
  • context는 간단하게 state 보관함이라고 생각하면 된다.
...

let context1 = createContext();

function App() {
  
...

App.js
2. <Context>로 원하는 컴포넌트 감싸기

  • state 공유를 원하는 컴포넌트를 Context로 감싼다.

App.js
3. 공유하고 싶은 state를 value 속성에 담는다.

  • value={{ state1, state2 ... }}
...

<Route path="/detail/:id" element={
	<context1.Provider value={{ stocks }}>
		<Detail shoes={shoes}></Detail>
	</context1.Provider>
}></Route>

...

Detail.js
4. 쓰고 싶은 컴포넌트에서 Context를 import 한다.

import { Context1 } from './../App.js'

App.js
5. 해당 Context를 export(1번에서 해줘도 된다.)

export let context1 = createContext();

Detail.js
6. useContext(Context)로 공유된 state를 사용한다.

  • useContext는 보관함을 해체해주는 훅이라고 보면 된다.
    • 지금은 shoes가 Context1에 담겨져 왔다.
    • useContext(Context1)해주면 Context1을 해체해서 shoes를 쓸 수 있게 된다.
    • object 형태를 반환한다. 즉, {shoes, state2, state3 ...}와 같은 형식으로 반환해준다.
    • object 그대로 받거나, destructuring 이용해서 받는다.
function Detail(props) {
    // let stocks = useContext(Context1);
    let {stocks} = useContext(Context1);
  • Detail.js 파일에서 Detail 컴포넌트 뿐만 아니라, 그 자식인 TabContent 컴포넌트에서도 stocks가 사용 가능해진다.
function TabContent(props) {
    let {stocks} = useContext(Context1);
    
    let [fade, setFade] = useState('');

    useEffect(() => {
        let timer = setTimeout(() => { setFade('end')}, 10);
        
        return () => {
            clearTimeout(timer);
            setFade('');
        }
    }, [props.tab])
    
    return (
        <div className={'start ' + fade}>
        {/* <div className={`start ${fade}`} */}
            {stocks}
            {[<div>내용0</div>, <div>내용1</div>, <div>내용2</div>][props.tab]}
        </div>
    )
}

Context API의 단점

  • Context API를 안 쓰는 이유
    • state 변경 시 쓸데 없는 것까지 재렌더링된다.
      • Detail 컴포넌트에서 stocks를 전혀 사용하고 있지 않은 상태에서 stocks가 변경된다면, Detail 컴포넌트가 재렌더링 되지 않는 것이 맞다.(Context API를 사용하고 있지 않을 때)
      • 허나 지금은 Context API를 사용하고, Context Provider로 Detail을 감싼 상태에서 공유할 state로 stocks를 골랐다면, stocks가 바뀌었을 때 Context Provider로 감싸진 Detail도 재렌더링 된다.(stocks를 공유하기 위해 Context Provider로 Detail을 감쌌지만, Detail에서 stocks를 사용하고 있지 않더라도 App 컴포넌트에서 stocks가 변경된다면 Detail 컴포넌트까지 재렌더링 된다)
  • 나중에 컴포넌트 재사용이 어려워진다.
    • 다른 페이지에서 TabContent 컴포넌트를 import 해서 재사용하고 싶을 때,
      • 그 페이지에서는 Context1과 같은 변수가 없다고 뜰 수 있다.
      • 즉, 관리가 어려워지게 된다.
profile
메타몽 닮음 :) email: alohajune22@gmail.com

0개의 댓글