REACT

DAY 76 - React - Hook: useEffect / 컴포넌트별 CSS (2024.10.24)

summ.n 2024. 10. 24. 18:21

SPA(Single Page Application)

컴포넌트 - App.js (메인화면 단을 가리키는 함수 하나하나를) -- 메인함수 (화면 꾸며주는 역할)

TestMain.jsx TestInput.jsx 전부 컴포넌트 -- 독립적으로 잡혀있기 때문에 재사용 목적으로 언제든지 다시 부를 수 있다.

 

클래스 컴포넌트는 사라짐 / 함수형 컴포넌트만 보면 된다.

 

JSX 규칙을 따라간다. 자바스크립트와 XML이 섞임

return 안에 있는 구역이 전부 jsx 구역

예전에는 바벨을 깔아서 직접 교환하라 했지만 지금은 노드가 알아서 해준다.

 

Props -- 데이터 전달 

useState -- 현재 갖고 있는 값을 바꾸고싶다.

useRef -- 현재 갖고있는 값 바꾸고싶지 않다.

 

이제 숙제 검사 !! 어제 블로그로 가자.


Test06.jsx

            <h3>이름 : { name }</h3>
            <h3>아이디 : { id }</h3>
            <h3>비밀번호 : { pwd }</h3>

데이터 찍어보는거 추가하기

 

 

const onInput = (e) => {
        const {name, value} = e.target;

        setDto({
            ...dto, //먼저 객체를 복사하고
            [name] : value //원하는 값(name or id or pwd)만 수정한다.

                                    //복사하지 않으면 이전 값은 초기화 된다.
        });
}

 [name] : value 여기서 name을 문자열이 아니라 변수로 사용하기 위해서 "name"에서 ""를 빼주는 역할이 

[ ] 이라고 보면 된다.

import React from 'react';
import { useRef } from 'react';
import { useState } from 'react';

const Test06 = () => {
    const nameRef = useRef();
    const [dto, setDto] = useState({
        name: '',
        id: '',
        pwd: ''
    });
    const {name, id, pwd} = dto;

    const onInput = (e) => {
        const {name, value} = e.target;

        setDto({
            ...dto, //먼저 객체를 복사하고
            [name] : value //원하는 값(name or id or pwd)만 수정한다.
                           //복사하지 않으면 이전 값은 초기화 된다.
        });
    }

    const onReset = () => {
        setDto({
            name: '',
            id: '',
            pwd: '' 
        });

        nameRef.current.focus();
    }

    return (
        <div>
            <table border={1} cellPadding={5} cellSpacing='0'>
                <tr>
                    <th width='100'>이름</th>
                    <td><input type='text' name='name' value={name} onChange={ onInput } ref={nameRef}/></td>
                </tr>
                <tr>
                    <th width='100'>아이디</th>
                    <td><input type='text' name='id' value={id} onChange={ onInput }/></td>
                </tr>
                <tr>
                    <th width='100'>비밀번호</th>
                    <td><input type='password' name='pwd' value={pwd} onChange={ onInput }/></td>
                </tr>

                <tr>
                    <td colSpan='2' align='center'>
                       <button onClick={ onReset }>초기화</button>
                    </td>
                </tr>
            </table>
            <hr/>
            <h3>이름 : { name }</h3>
            <h3>아이디 : { id }</h3>
            <h3>비밀번호 : { pwd }</h3>
        </div>
    );
};

export default Test06;

 


- public에 있는 이미지 폴더는 index.html를 기준으로부터 상대경로를 지정해야 한다.

- index.html 안의 <div id="root"></div> 이곳으로 렌더링 되기 때문이다.

- 단 ./ 를 생략해서는 안 된다.


   

                                             Test08Gallery.jsx

                   Test08Data.jsx                                       Test08View.jsx

                                                          Test08Big.jsx                        Test08List.jsx

                                                     (한 장의 사진은 크게)                  Test08Item.jsx

 

{ id: 1, img: './image/dog01.jpg', title: '' },

화면에 띄워주는건 index.html으로 띄워주는 것이므로 public 입장에 두고 거기에 image다 해서 들어와야한다.

헷갈리면 안 된다. 최종으로 뜨는건 index.html이므로 저 div가 화면에 보이는 곳이다.

그러므로 image폴더 위치를 잘 생각해야함 !!!


Test08Data.jsx

export default [
    { id: 1, img: './image/dog01.jpg', title: '시무룩한 골든리트리버' },
    { id: 2, img: './image/dog02.jpg', title: '시무룩한 시바견' },
    { id: 3, img: './image/dog03.jpg', title: '꼬질한 강아지' },
    { id: 4, img: './image/dog04.jpg', title: '새하얀 포메라니언' },
    { id: 5, img: './image/dog05.jpg', title: '하찮게 귀여운 강아지' },
    { id: 6, img: './image/dog06.jpg', title: '눈 감고있는 강아지' },
    { id: 7, img: './image/dog07.jpg', title: '귀여운 푸들' },
    { id: 8, img: './image/dog08.jpg', title: '귀 쫑긋 강아지' }
]

우리의 위치는 index.html이므로 ./ 현재위치에서 image폴더에 있는 사진들 가져와라 하는 것이다 !!

Test08Data.jsx에서 image 폴더로 가는게 아니다 !!!!!


Test08Gallery.jsx

Test08Data.jsx에서 export한 데이터리스트 받자 !!

import React from 'react';
import dataList from './Test08Data';
import { useState } from 'react';
import Test08View from './Test08View';

import '../css/Test08.css'

const Test08Gallery = () => {
    const [list, setList] = useState(dataList);

    return (
        <div className='wrap2'>
            <Test08View list={list}/>
        </div>
    );
};

export default Test08Gallery;

 

list 들고 Test08View.jsx로 가자 !!


Test08View.jsx

import React from 'react';
import Test08Big from './Test08Big';
import Test08List from './Test08List';

const Test08View = ({list}) => {
    return (
        <div className='bigview'>
            <Test08Big/>
            <Test08List list={list}/>
        </div>
    );
};

export default Test08View;

Test08List.jsx로 list 들고가자 !!


Test08List.jsx

받은 list를 하나씩 해서 Test08Item.jsx로 보내자 !!

import React from 'react';
import Test08Item from './Test08Item';

const Test08List = ({list}) => {
    return (
        <ul className='list'>
            {
                list.map(item => <Test08Item key={item.id} item={item}/>)
            }   
        </ul>
    );
};

export default Test08List;

Test08Item.jsx

import React from 'react';

const Test08Item = ({item}) => {
    const{id, img, title} = item

    return (
        <li>
            <img src={img} alt={title}/>
        </li>
    );
};

export default Test08Item;


위에 이미지 한 개가 크게 보이게 하는 것을 하고싶다 !!

Test08View.jsx

import React from 'react';
import Test08Big from './Test08Big';
import Test08List from './Test08List';

const Test08View = ({list}) => {
    return (
        <div className='bigview'>
            <Test08Big/>
            <Test08List list={list}/>
        </div>
    );
};

export default Test08View;

여기서 Test08Big으로 갈 때 하나의 이미지를 가지고 가야하는데

데이터를 쥐고 있는게 TestGalley이다.

대표적으로 하나의 이미지만 쥐고있는게 필요하다 !!


Test08Gallery.jsx

여기서 하나의 이미지만 가지고 있는 게 필요하다.

const[one, setOne] = useState(list[1]); 

 

<Test08View list={list} one={one}/>

one 전달해준다.

import React from 'react';
import dataList from './Test08Data';
import { useState } from 'react';
import Test08View from './Test08View';

import '../css/Test08.css'

const Test08Gallery = () => {
    const [list, setList] = useState(dataList);
    const[one, setOne] = useState(list[1]);

    return (
        <div className='wrap2'>
            <Test08View list={list} one={one}/>
        </div>
    );
};

export default Test08Gallery;

Test08View.jsx

const Test08View = ({list, one}) => {

one 받아주고

 

<Test08Big one={one}/>

one을 보낸다.

import React from 'react';
import Test08Big from './Test08Big';
import Test08List from './Test08List';

const Test08View = ({list, one}) => {
    return (
        <div className='bigview'>
            <Test08Big one={one}/>
            <Test08List list={list}/>
        </div>
    );
};

export default Test08View;

Test08Big.jsx

import React from 'react';

const Test08Big = ({one}) => {
    const {id, img, title} = one
    return (
        <div className='bigimg'>
            <h2>{title}</h2>
            <img src={img} alt={title} />
        </div>
    );
};

export default Test08Big;


이제 클릭하면 저 큰 이미지가 바뀌게 하기 !!

 

Test08Item.jsx -- 여기가 시작 !!! 이미지 누르면 onView 함수를 잡아줬다.

import React from 'react';

const Test08Item = ({item, onView}) => {
    const{id, img, title} = item

    return (
        <li onClick={ onView }>
            <img id={id} src={img} alt={title}/>
        </li>
    );
};

export default Test08Item;

 

 

                                               Test08Gallery.jsx (onView)

                   Test08Data.jsx                                       Test08View.jsx (onView)

                                                          Test08Big.jsx                        Test08List.jsx (onView)

                                                     (한 장의 사진은 크게)                  Test08Item.jsx(onView)

 

이 구조를 잘 생각해보면 저 onView함수는 최상단에 있는 부모 Test08Gallery.jsx부터 타고타고 보내야함 !


Test08Gallery.jsx

const onView = (e) => {
        console.log(e.target);
        const{id} = e.target; -- 고유한 키 값이 필요 !! id를 이용 !!
        setOne(list[id-1]) -- 대신 그냥 id하면 배열에서는 0부터 시작이므로 id-1을 해줘야한다 !
}

import React from 'react';
import dataList from './Test08Data';
import { useState } from 'react';
import Test08View from './Test08View';

import '../css/Test08.css'

const Test08Gallery = () => {
    const [list, setList] = useState(dataList);
    const[one, setOne] = useState(list[0]);

    const onView = (e) => {
        console.log(e.target);
        const{id} = e.target;
        setOne(list[id-1])
    }

    return (
        <div className='wrap2'>
            <Test08View list={list} one={one} onView={onView}/>
        </div>
    );
};

export default Test08Gallery;

Test08View.jsx

import React from 'react';
import Test08Big from './Test08Big';
import Test08List from './Test08List';

const Test08View = ({list, one, onView}) => {
    return (
        <div className='bigview'>
            <Test08Big one={one}/>
            <Test08List list={list} onView={onView}/>
        </div>
    );
};

export default Test08View;

Test08List.jsx

import React from 'react';
import Test08Item from './Test08Item';

const Test08List = ({list, onView}) => {
    return (
        <ul className='list'>
            {
                list.map(item => <Test08Item key={item.id} item={item} onView={onView}/>)
            }   
        </ul>
    );
};

export default Test08List;

Test08Item.jsx --- 최종으로 여기로 온다 !!

import React from 'react';

const Test08Item = ({item, onView}) => {
    const{id, img, title} = item

    return (
        <li onClick={ onView }>
            <img id={id} src={img} alt={title}/>
        </li>
    );
};

export default Test08Item;

 

이미지 누를 때마다 바뀐다 !!


src 안 이미지 파일 처리

src 안에 있는 이미지 파일 처리는 참조변수를 사용한다.


[형식]

 import 참조변수 from '이미지경로';


이번엔 src 안에 image 폴더를 잡아보자 !

Test09.jsx

import React from 'react';

import img01 from '../image/img01.jpg'
import img02 from '../image/img02.jpg'
import img03 from '../image/img03.jpg'
import img04 from '../image/img04.jpg'
const Test09 = () => {
    return (
        <div>
            <img src={img01} alt='짱구01' style={{width: '200px', height: '200px'}} /> &emsp;
            <img src={img02} alt='짱구02' style={{width: '200px', height: '200px'}} /> &emsp;
            <img src={img03} alt='짱구03' style={{width: '200px', height: '200px'}} /> &emsp;
            <img src={img04} alt='짱구04' style={{width: '200px', height: '200px'}} /> &emsp;
        </div>
    );
};

 


Test10.jsx

이제는 데이터파일을 가져오는게 아니라 추가버튼을 누를 때마다 데이터가 쌓이게 하고싶은거 !! (list에 쌓이게 !)

 

const [list, setList] = useState([]);

지금은 배열 값 비어있다는 것 !! 하나도 안 갖고있다.

 

            <ul>
                {
                    list.map((item, index) => <li key={ index }>
                        { item.name } / { item.age }
                    </li>)
                }
            </ul>

이렇게해서 입력한 값 찍어주려고 하는 것 !

 

<form onSubmit={ onAdd }>

onAdd 함수 잡으러 가자 !!

 

submit은 페이지를 이동시키는 것이다. 넘어가려고 한다.

이런식으로 넘어가버리니까 못가게 할 것이다.

 

const onAdd = (e) => {
        e.preventDefault(); // 브라우저의 고유한 동작을 중단시키는 역할
}

이제 추가 버튼을 눌러도 넘어가지 않는다.

 

배열은 수정이 아니라 추가다 !!!!

const onAdd = (e) => {
        e.preventDefault(); // 브라우저의 고유한 동작을 중단시키는 역할

        if(!name || !age) 
            return;

        setList([
            ...list,
            dto
        ]);
}
    const onAdd = (e) => {
        e.preventDefault(); // 브라우저의 고유한 동작을 중단시키는 역할

        if(!name || !age) 
            return;

        setList([
            ...list,
            {
                name: name,
                age: age
            }
        ]);
    }
    const onAdd = (e) => {
        e.preventDefault(); // 브라우저의 고유한 동작을 중단시키는 역할

        if(!name || !age) 
            return;

        setList([
            ...list,
            {
                name,
                age
            }
        ]);
    }

3개 다 같은 결과 !!


값을 입력하고 또 하려고 하면 입력창 값을 초기화 시켜줘야한다 !!

        //초기화
        setDto({
            name: '',
            age: ''
        })


입력하고 나면 이름에 focus가게하기 !

import { useRef } from 'react';

const nameRef = useRef();

이름 : <input type='text' name='name' value={ name } onChange={ onInput } ref={nameRef}/>

nameRef.current.focus();


얘네는 유일한 키값이 없다. 그러므로 똑같이 홍길동을 두 번 추가해도 상관이 없어진다.

그래서 이 때 필요한게  ref이다.

const seq = useRef(1); //1 2 3 4 5 6 7 ~~~

이렇게 잡으면 숫자가 순서대로 커진다.

 

        setList([
            ...list,
            {
                seq: seq.current++,
                name,
                age
            }
        ]);

값을 넣을 때마다 seq.current++ 하면 증가하게 된다.

앞에 이름은 맘대로!!

 

 

            <ul>
                {
                    list.map((item, index) => <li key={ index }>
                        {item.seq} / { item.name } / { item.age }
                    </li>)
                }
            </ul>

import React from 'react';
import { useRef } from 'react';
import { useState } from 'react';

const Test10 = () => {
    const nameRef = useRef();
    const seq = useRef(1); //1 2 3 4 5 6 7 ~~~

    const [dto, setDto] = useState({ //1인분
        name: '',
        age: '',
    });
    const{ name, age } = dto;

    const [list, setList] = useState([]);

    const onInput = (e) => {
        console.log(e.target);
        const {name, value} = e.target;

        setDto({
            ...dto, 
            [name] : value
        });
    }

    const onAdd = (e) => {
        e.preventDefault(); // 브라우저의 고유한 동작을 중단시키는 역할

        if(!name || !age) 
            return;

        setList([
            ...list,
            {
                seq: seq.current++,
                name,
                age
            }
        ]);

        //초기화
        setDto({
            name: '',
            age: ''
        })

        nameRef.current.focus();
    }

    return (
        <div>
            <form onSubmit={ onAdd }>
                이름 : <input type='text' name='name' value={ name } onChange={ onInput } ref={nameRef}/>
                <br/>
                나이 : <input type='text' name='age' value={ age } onChange={ onInput }/>
                <br/>
                <button type='submit'>추가</button>
            </form>
            <hr/>
            <ul>
                {
                    list.map((item, index) => <li key={ index }>
                        {item.seq} / { item.name } / { item.age }
                    </li>)
                }
            </ul>
        </div>
    );
};

export default Test10;

 


★Test11.jsx

헷갈린다,,, 복습하기 !!!!!!!

import React from 'react';
import { useState } from 'react';

const dataList = [
    { id: 1, name: 'chk1', text: '연령(만 14세 이상) 확인(필수)', isChk: false},
    { id: 2, name: 'chk2', text: '개인정보 위탁 처리 동의(필수)', isChk: false},
    { id: 3, name: 'chk3', text: '개인정보 수집 및 이용에 대한 동의(필수)', isChk: false},
    { id: 4, name: 'chk4', text: '이벤트 우대 혜택 동의 안내(선택)', isChk: false},
]

const Test11 = () => {
    const [list, setList]= useState(dataList);

    const onChk = (e) => {
        const {name, checked} = e.target;
        if(name === 'all'){
            setList(list.map(item => {
                return {
                    ...item, //밖을 { }로 감싸고 있는 객체이므로 수정이다. 
                    isChk: checked //나머지애들은 유지하되 isChk만 바꾼다.
                }
            }))
        }else{
            //setList(list로 map을 돌리면서 선택한 name과 같은 것을 찾아서 isChk로 변경한다.)     
            setList(list.map(item => item.name === name ? {...item, isChk: checked } : item ));
        }
    }

    return (
        <div>
            <p>
                <input type='checkbox' name='all'onChange={ onChk }/>
                <label>약관동의</label>
            </p>
            <hr/>
            {
                list.map(item => <p key={ item.id }>
                    <input type='checkbox' name={ item.name } checked={ item.isChk } onChange={ onChk }/>
                    <label>{ item.text }</label>    
                </p>)
            }
        </div>
    );
};

export default Test11;


밑에 항목이 일부라도 체크가 취소되면 전체선택이 풀려야한다.

전체선택 checked가 false가 되어야 전체선택이 풀리므로 !! 

false인 조건을 체크해야한다 

        <p>
            <input type='checkbox' name='all'onChange={ onChk }
            checked={ list.filter(item => item.isChk !== true ).length < 1 }/>
            <label>약관동의</label>
        </p>

이건 추천 X

    <input type='checkbox' name='all'onChange={ onChk }
    checked={ list.filter(item => !item.isChk).length < 1 }/>

true인지 물어보지 말고 !! ! 느낌표 연산자로 체크하기 !!

 

    <input type='checkbox' name='all'onChange={ onChk }
    checked={ list.filter(item => item.isChk).length === list.length }/>

이 방법이 더 좋을듯?

 


전체코드

import React from 'react';
import { useState } from 'react';

const dataList = [
    { id: 1, name: 'chk1', text: '연령(만 14세 이상) 확인(필수)', isChk: false},
    { id: 2, name: 'chk2', text: '개인정보 위탁 처리 동의(필수)', isChk: false},
    { id: 3, name: 'chk3', text: '개인정보 수집 및 이용에 대한 동의(필수)', isChk: false},
    { id: 4, name: 'chk4', text: '이벤트 우대 혜택 동의 안내(선택)', isChk: false},
]

const Test11 = () => {
    const [list, setList]= useState(dataList);

    const onChk = (e) => {
        const {name, checked} = e.target;
        if(name === 'all'){
            setList(list.map(item => {
                return {
                    ...item, //밖을 { }로 감싸고 있는 객체이므로 수정이다. 
                    isChk: checked //나머지애들은 유지하되 isChk만 바꾼다.
                }
            }))
        }else{
            //setList(list로 map을 돌리면서 선택한 name과 같은 것을 찾아서 isChk로 변경한다.)     
            setList(list.map(item => item.name === name ? {...item, isChk: checked } : item ));
        }
    }

    return (
        <div>
            <p>
                <input type='checkbox' name='all'onChange={ onChk }
                checked={ list.filter(item => item.isChk).length === list.length }/>
                <label>약관동의</label>
            </p>
            <hr/>
            {
                list.map(item => <p key={ item.id }>
                    <input type='checkbox' name={ item.name } checked={ item.isChk } onChange={ onChk }/>
                    <label>{ item.text }</label>    
                </p>)
            }
        </div>
    );
};

export default Test11;

day04

cd day04

npm start

 


Hooks - useEffect?

- useEffect는 렌더링, 혹은 변수의 값 혹은 오브젝트가 달라지게 되면,

  그것을 인지하고 업데이트를 해주는 함수이다.

 

- useEffect는 콜백 함수를 부르게 되며, 렌더링 혹은 값, 오브젝트의 변경에 따라

  어떠한 함수 혹은 여러 개의 함수들을 동작시킬 수 있다.

 

- 렌더링 후 useEffect는 무조건 한번은 실행된다.

 

useState와 useEffect는 같이 쓰인다.

 

[형식]

컴포넌트가 나타날 때 1번만 함수가 호출

useEffect( () => {

}, [ ]);

 

특정 props가 바뀔 때마다 함수가 호출

useEffect( () => {

}, [ props ]);

 

useEffect 라는 Hook을 사용하여 할 수 있는 3가지 동작

- 컴포넌트가 마운트 됐을 때 (처음 나타났을 때)

- 언 마운트 됐을 때 (사라질 때)

- 업데이트될 때 (특정 props가 바뀔 때)

 

[ ]로 설정하면 컴포넌트가 처음 나타날 때만 useEffect에 등록한 함수가 호출 한다.

 

useEffect 에서는 함수를 반환 할 수 있는데 이를 cleanup 함수라고 부른다.

cleanup 함수는 useEffect 에 대한 뒷정리를 해준다고 이해하면 되는데, [ ] 안에 내용이 비어 있는 경우에는 컴포넌트가 사라질 때 cleanup 함수가 호출된다.

 

//e.clientX, e.clientY

//브라우저에서 사용자에게 웹페이지가 보여지는 영역을 기준으로 좌표를 표시

 

함수형 업데이트

- 비동기적인 방법을 해결하기 위해서 우리는 함수형 업데이트(functional update)를 사용할 수 있다.

setState에 값을 그대로 전달하는 것이 아니라 함수를 전달하는 것이다.


Test01.jsx

const name = '카리나,아이유,수지,공유,이동욱,윈터,김세정,이제훈,설인아,유연석,안보현'.split(',');

쉼표로 하나하나 잘리면서 name[0] name[1] 배열로 잡힌다.

import React from 'react';
import { useState } from 'react';

const Test01 = () => {
    const names = '카리나,아이유,수지,공유,이동욱,윈터,김세정,송강,한소희,차은우,안보현'.split(',');
    const [name, setName] = useState('홍길동');
    const [age, setAge] = useState(25);

    const onName = () => {
        const index = Math.floor(Math.random() * 11) //버림, 0 ~ 10
        setName(names[index])
    }

    const onAge = () => {
        setAge(age + 1)
    }   

    return (
        <div>
            <button onClick={ onName }>이름 변경</button>
            <button onClick={ onAge }>나이 변경</button>
            <hr/>
            <h1>{ name }</h1>
            <h1>{ age }</h1>
        </div>
    );
};

export default Test01;


    useEffect(() => {
        console.log('안녕하세요');
    });

처음실행하자마자

마운트 되자마자 저걸 실행해서 안녕하세요가 찍히게된다.

근데 왜 2번이 찍힐까??

 

 

실행하면 콘솔 창에 UseEffect는 왜 두 번 실행되는 걸까?

 

[해결 방법]

index.js

=> <React.StrictMode> 부분을 주석으로 처리

 

const root = ReactDOM.createRoot(document.getElementById('root'));

root.render(

//<React.StrictMode>

<App />

//</React.StrictMode>

);

 

ReactStrict Mode ?

StrictMode는 리액트에서 제공하는 검사 도구이다.

개발 모드일 때 디버그를 통하여, 이 태그로 감싸져있는 App 컴포넌트와 자손까지 검사하는 것이다.

안전하지 않은 생명 주기를 가진 컴포넌트, 권장되지 않은 부분, 배포 후 문제가 될 수 있는 부분들까지 미리 확인하는 것이다.

 

creat-react-app으로 앱을 만들었기 때문에 기본적으로 생성되어 랜더링을 두 번이나 했었던 것이다.

 

index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  //<React.StrictMode>
    <App />
  //</React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

App만 렌더링되게 하면 된다.

한 번만 나오게 된다 !!


값이 바뀔 때마다 useEffect는 계속 실행되기 때문에 계속 안녕하세요가 찍혀서 나온다 !!

변화가 있을 때마다 사용되니 과부하가 일어나서 useEffect는 거의 사용하지 않는다.

 

마운트가 될 때 1번만 실행하고싶으면 ???

    useEffect(() => {
        console.log('안녕하세요');
    }, [ ]);

이렇게하면 된다 !

그럼 처음에만 한 번 안녕하세요가 찍히고 값이 바뀌더라도 안녕하세요가 더이상 찍히지 않는다.


마운트 될 때랑 name값이 바뀔 때 실행하고싶으면?

    useEffect(() => {
        console.log('안녕하세요');
    }, [name]);   

나이 바뀔 때는 실행안되고 이름이 바뀔 때만 실행 된다 !!


전체코드

import React from 'react';
import { useEffect } from 'react';
import { useState } from 'react';

const Test01 = () => {
    const names = '카리나,아이유,수지,공유,이동욱,윈터,김세정,송강,한소희,차은우,안보현'.split(',');
    const [name, setName] = useState('홍길동');
    const [age, setAge] = useState(25);

    const onName = () => {
        const index = Math.floor(Math.random() * 11) //버림, 0 ~ 10
        setName(names[index])
    }

    const onAge = () => {
        setAge(age + 1)
    }   

    //거의 사용X
    //값이 바뀌기만 하면 실행된다.
    //불필요한 사용이 많다.
    /*
    useEffect(() => {
        console.log('안녕하세요');
    });
*/
/*
    useEffect(() => {
        console.log('안녕하세요');
    }, []);
*/

    //마운트, name값이 바뀔 때만 실행
    useEffect(() => {
        console.log('안녕하세요');
    }, [name]);   

    return (
        <div>
            <button onClick={ onName }>이름 변경</button>
            <button onClick={ onAge }>나이 변경</button>
            <hr/>
            <h1>{ name }</h1>
            <h1>{ age }</h1>
        </div>
    );
};

export default Test01;

Test02Sub.jsx

import React from 'react';
import { useState } from 'react';

const Test02Sub = () => {
    const [x, setX] = useState(0);
    const [y, setY] = useState(0);

    return (
        <div>
            <h2>마우스 좌표</h2>
            <div style={{border: '1px solid #000', width: 400, padding: 30, margin: 15}}>
                <h3>X축 : { x }, Y축 : { y }</h3>
            </div>
        </div>
    );
};

export default Test02Sub;

Test02.jsx

import React from 'react';
import Test02Sub from './Test02Sub';

const Test02 = () => {
    return (
        <div>
            <Test02Sub/>
        </div>
    );
};

export default Test02;


이제 여기서 마우스가 움직일 때마다 좌표값이 바뀌게 할 것이다.

이 때 마우스라는 이벤트는 윈도우에서 일어나는 이벤트인데 이 이벤트를 어디서 생성할 것이냐??

Test02Sub.jsx에서 useEffect를 사용해야한다.

    useEffect(() => {
        console.log('useEffect');
        window.addEventListener('mousemove', onMove); //이벤트(윈도우 환경에서 이벤트를 발생하는 거 체크한다는 말)
   
        //[]로 지정하면, 컴포넌트가  사라질 때 cleanup 함수가 호출된다.
    }, []);

 

 

mousemove 이벤트가 일어나면 onMove 함수가 일어나게 !!

 

onMove함수 잡아주러 가자 !

    const onMove = (e) => {
        //브라우저에서 사용자가 웹페이지에 보여지는 영역을 기준으로 표시
        setX(e.clientX);
        setY(e.clientY);
    }

 


    useEffect(() => {
        console.log('useEffect');
        window.addEventListener('mousemove', onMove); //이벤트(윈도우 환경에서 이벤트를 발생하는 거 체크한다는 말)
   
        //[]로 지정하면, 컴포넌트가  사라질 때 cleanup 함수가 호출된다.
        return () => {
            console.log('cleanup');
            window.removeEventListener('mousemove', onMove)
        }
    }, []);

 

[ ]로 지정하면, 컴포넌트가  사라질 때 cleanup 함수가 호출된다.

서블릿 

주기함수

init()

doGet() / doPost()

destroy -- cleanup함수와 같은 역할

 

컴포넌트는 계속 살아있기 때문에 컴포넌트가 나왔다 사라졌다 하게 해야한다.

import React from 'react';
import Test02Sub from './Test02Sub';
import { useState } from 'react';

const Test02 = () => {
    const [show, isShow] = useState(false);

    const onToggle = () => {
        isShow(!show);
    }
    
    return (
        <div>
            <button onClick={ onToggle }>{show ? '숨기기' : '보이기'}</button>
            {
                show && <Test02Sub/>
            }
        </div>
    );
};

export default Test02;


Test02Sub.jsx - 전체코드

import React from 'react';
import { useEffect } from 'react';
import { useState } from 'react';

const Test02Sub = () => {
    const [x, setX] = useState(0);
    const [y, setY] = useState(0);

    const onMove = (e) => {
        //브라우저에서 사용자가 웹페이지에 보여지는 영역을 기준으로 표시
        setX(e.clientX);
        setY(e.clientY);
    }

    useEffect(() => {
        console.log('useEffect');
        window.addEventListener('mousemove', onMove); //이벤트(윈도우 환경에서 이벤트를 발생하는 거 체크한다는 말)
   
        //[]로 지정하면, 컴포넌트가  사라질 때 cleanup 함수가 호출된다.
        return () => {
            console.log('cleanup');
            window.removeEventListener('mousemove', onMove)
        }
    }, []);

    return (
        <div>
            <h2>마우스 좌표</h2>
            <div style={{border: '1px solid #000', width: 400, padding: 30, margin: 15}}>
                <h3>X축 : { x }, Y축 : { y }</h3>
            </div>
        </div>
    );
};

export default Test02Sub;

컴포넌트별로 CSS 작성
- 파일명.module.css
- import 참조변수 form './파일명.module.css';
- 개발자 도구에서 보면 '파일명클래스명아무말' 이라고 설정된다

 

                                               Test08Gallery.jsx 

                   Test08Data.jsx                                       Test08View.jsx 

                                                          Test08Big.jsx                        Test08List.jsx

                                                     (한 장의 사진은 크게)                  Test08Item.jsx

Test08Gallery.jsx에서 css를 적용하면 모든 데에 다 적용이 된다.

하지만 개별 jsx에 css를 적용하고싶다면!?

파일명.module.css 이런식으로 하는 것이다.

 

대신 읽을 때는 import 참조변수 form './파일명.module.css'; 이렇게 읽어야한다.

그리고 개발자 도구에서 보면 '파일명클래스명아무말' 이라고 설정된다.


Test03.css

.box {
    width: 400px; 
    height: 100px; 
    margin: 20px; 
    background: hotpink; 
    border-radius: 25px;
    box-sizing: 10px 10px 0 #dcdcdc; 
    text-align: center; 
    line-height: 100px; 
    text-transform: uppercase; 
    font-size: 40px; 
    font-weight: 600; 
    letter-spacing: -1px;
}

Test03.jsx

import React from 'react';
import '../css/Test03.css'

const Test03 = () => {
    return (
        <div>
            <div className='box'>Test</div>
            <div>Test</div>
        </div>
    );
};

export default Test03;


이제 나만의 css를 만들고싶으면 Test03.module.css 이렇게 이름을 지으면 된다.

Test03.module.css

.box {
    width: 500px; 
    height: 80px; 
    margin: 20px; 
    background: tomato; 
    border-radius: 25px;
    box-sizing: 10px 10px 0 #dcdcdc; 
    text-align: center; 
    line-height: 80px; 
    text-transform: uppercase; 
    font-size: 40px; 
    font-weight: 600; 
    letter-spacing: -1px;
}

Test03.jsx

import React from 'react';
import '../css/Test03.css'
import myStyle from '../css/Test03.module.css';

const Test03 = () => {
    return (
        <div>
            <div className='box'>Test</div>
            <div className={myStyle.box}>Test</div>
        </div>
    );
};

export default Test03;

Test03.css의 영향은 받지 않는 것을 확인할 수 있다 !!

 

이런식으로 아무말이나 적히는 거 확인할 수 있다 !!


                         App.jsx                                                                              Todos

Test03.jsx                             Test04.jsx                            TodoForm                             TodoList           

Test03Sub.jsx                                                                                                                TodoItem

 

App.jsx 

import React from 'react';
import Test01 from './components/Test01';
import Test02 from './components/Test02';
import Test03 from './components/Test03';
import Test04 from './components/Test04';
import Test03Sub from './components/Test03Sub';

const App = () => {
  return (
    <div>
      {/*<Test01/>*/}
      {/*<Test02/>*/}
      <Test03/>
      <Test03Sub/>
      <Test04/>
    </div>
  );
};

export default App;

Test03Sub.jsx 

import React from 'react';

const Test03Sub = () => {
    return (
        <div>
            <div className='box'>Test</div>
        </div>
    );
};

export default Test03Sub;

Test04.jsx 

import React from 'react';

const Test04 = () => {
    return (
        <div>
            <div className='box'>Test</div>
        </div>
    );
};

export default Test04;

Test03.jsx 

import React from 'react';
import '../css/Test03.css'
import myStyle from '../css/Test03.module.css';
import Test03Sub from './Test03Sub';

const Test03 = () => {
    return (
        <div>
            <div className='box'>Test</div>
            <div className={myStyle.box}>Test</div>
            <Test03Sub/>
        </div>
    );
};

export default Test03;

SPA로 한 페이지로 다 들어오기 때문에 여기서 import로 css를 잡지 않아도 알아서 css가 적용되는 것을 볼 수 있다.