mjeongriver
article thumbnail
Published 2023. 1. 18. 18:34
day68-react TIL/React

1. 리액트 기본 훅

* hook이란?

- 리액트 컴포넌트는 클래스형 컴포넌트(Class component)와 함수형 컴포넌트(Functional component)로 나뉩니다.

- 리액트 훅은 새로운 기능으로 React 16.8버전에 새로 추가된 기능

- 함수형태의 컴포넌트에서 사용되는 몇가지 기술을 Hook이라고 부른다. (useState, userEffect 등)

- 리액트 훅은 함수형 컴포넌트가 클래스형 컴포넌트의 기능을 사용할 수 있도록 해주는 기능이다.

 

* 훅의 규칙

- 최상위 에서만 Hook을 호출해야 한다

- 반복문, 조건문, 중첩된 함수 내에서 Hook을 실행하면 안된다.

- 이 규칙을 따르면 컴포넌트가 렌더링될 때마다 항상 동일한 순서로 Hook이 호출되는 것이 보장된다.

- 리액트 함수 컴포넌트에서만 Hook을 호출해야 한다.

 

* 반드시 알아야 할 기본 훅

useState(초기값) 

- useState() : 배열반환 

- 첫번째 배열의 요소에는 현재값을, 두번째 요소는 상태를 변경하는 (setter) 를 반환합니다.

const [data, setData] = useState('초기값')
useEffect(실행시킬 콜백함수, 값에 따른 렌더링 지정 ) 

 

- userEffect의 첫번째 매개변수는 실행시킬 콜백함수

- userEffect의 두번째 매개변수는 배열[]을 사용하여 특정값이 update 될 때만 실행시켜 줄 수 있습니다.

- useEffect() 는 컴포넌트의 라이프 사이클을 다룹니다.
- 리액트 컴포넌트가 mount, mount이후, unmount때 마다 특정작업을 수행합니다.

 

* 라이프 사이클

 

 

- mount 이후: 컴포넌트가 마운트 됨, 즉 컴포넌트의 첫번째 렌더링이 마치면 호출되는 메서드 입니다.

 - 클래스형 componentDidMount() 대체

함수형 훅

useEffect( () => {
	console.log(`렌더링완료`);
});

 

- mount 이후: 업데이트 될 때는 실행되지 않으려면, 두번째 매개변수 배열를 줍니다.

useEffect( () => {
	console.log(`처음만 실행됩니다`);
}, []);​

 

- update 이후: 특정값에 의해 컴포넌트가 업데이트 되고 난 후 발생합니다.

- 클래스형 componentDidUpdate() 대체

함수형 훅

const HookEffect = () => {
    //useState
    const[name, setName] = useState('');
    const handleName = (e) => {
        setName( e.target.value );
    }

    //특정값이 업데이트 될 때만 실행해주려면 두번째 매개변수에 값을 state값을 지정합니다
    useEffect( () => {
        console.log(`name이 업데이트 시 실행됩니다`)
    }, [name]);

    return (
        <div>
            
            이름:<input type="text" onChange={handleName}/><br/>
            이름:{name}
        </div>
    )
}

export default HookEffect;

 

- unmount직전: 컴포넌트가 화면에서 사라지기 직전에 호출됩니다.

- 클래스형 componentWillUnMount() 대체

함수형 

const HookEffect = () => {
    //useState
    const[name, setName] = useState('');
    const handleName = (e) => {
        setName( e.target.value );
    }
    
	//useEffect    
    useEffect( () => {
        console.log(`name이 업데이트 시 실행됩니다`)
        
        //unmount이후 실행됩니다.
        return () => {
            console.log(`unmount에 실행됩니다.`);
        }
    }, [name]);

    return (
        <div>
            이름:<input type="text" onChange={handleName}/><br/>
            이름:{name}
        </div>
    )
}

export default HookEffect;

-    특정 태그에 이름달기 useRef()

useRef(초기값) 
const 사용할이름 = useRef(null);

 

- 이벤트를 사용하다 보면 특정 태그에 접근해서 핸들링 하는 경우가 생깁니다.

- arrow function에 event 매개변수를 이용해서, 자신 태그에는 접근할 수 있지만, 다른태그는 핸들링 하기가 어렵습니다.

- 이런경우 useRef() 훅을 이용해서 특정태그에 이름을 지정하고 핸들링 할 수 있습니다.

 

const HookRef = () => {
    //useState
    const [form, setForm] = useState({data: '', result: '' });
    //useRef
    const inputTag = useRef(null);
    //인풋핸들링
    const handleChange = (e) => {
        setForm( {data :e.target.value} ); //인풋값이 변할때마다 data변수 변화
    }
    
    //버튼핸들링 - 클릭시 state는 변경하고, input태그에 포커스
    const handleClick = (e) => {
        setForm( {data: '', result: form.data})
        //useRef의사용 - current로 ref의 현재태그에 접근가능
        inputTag.current.focus()
    }

    return (
        <div>
            할일: <input type="text" value={form.data} onChange={handleChange} ref={inputTag} />
            <button type="button" onClick={handleClick}>등록하기</button>

            <br/>
            결과: {form.result}

        </div>
    )
}

export default HookRef;

 

* app.js, hookEffect.js, hookRef.js, hookReducer.js, hookReducer2.js, hookReducerComponent.js

import { useState } from "react";
import HookEffect from "./hook/HookEffect";
import HookRef from "./hook/HookRef";
import HookQ from "./hook/HookQ";
import HookQ2 from "./hook/HookQ2";
import HookReducer from "./hook/HookReducer";
import HookReducer2 from "./hook/HookReducer2";
const App = () => {

  /*
  p. 223
  1. 필수훅
  useState()
  컴포넌트에서 상태값을 제어하는 가장 기본이 되는 hook

  useEffect()
  컴포넌트의 라이프사이클(생명주기)를 다룹니다.
  mount, mount이후, state변경될 때, unmount 이전에 특정작업을 수행할 수 있습니다.
  
  useRef()
  태그의 이름 달기

  2. 부가적인 훅
  useReducer()
  useState의 사용을 외부에서 사용할 수 있게 해주는 훅입니다. (state의 변경을 외부에서 제어할 수 있습니다.)
  
  const [현재 값, 리듀서를 업데이트 할 수 있는 함수를 반환] = useReducer( 외부에서 사용할 리듀서 함수, 리듀서의 초기값)
 
  */

  const [visible, setVisible] = useState(true);

  const handleClick = () => {
    setVisible(!visible); //visible이 가진 값의 반대

  }


  return (
    <>
      {/* effter 훅 */}
      <button onClick={handleClick}>{visible ? "숨기기" : "보이기"}</button><br />
      {visible ? <HookEffect /> : <h3>ㅎㅇㅎㅇ</h3>}

      <hr/>
      {/* ref훅 */}
      <HookRef />

      {/* 훅 실습 */}
      <hr/>
      <HookQ />

      <hr/>
      <HookQ2 />

      {/* 리듀서 훅 */}
      <hr/>
      <HookReducer />

      <hr/>
      <HookReducer2 />

      
    </>
  )
}

export default App;

import { useEffect, useState } from "react";

const HookEffect = () => {
    //useState 훅
    const [name, setName] = useState('');
    const [age, setAge] = useState('');

    const handleName = (e) => {
        setName(e.target.value);
    }

    const handleAge = (e) => {
        setAge(e.target.value);
    }

    // useEffect(함수)
    // 화면이 mount된 이후에 동작함
    /* useEffect(() => {
        console.log(`렌더링완료. state값${name}`)
    }) */

    // useEffect(함수, []) - 화면이 첫번째 mount에서만 실행됨
    // useEffect(() => {
    //     console.log(`처음만 실행됩니다`);
    // }, [])

    // useEffect(함수, [state]) - 특정 값이 렌더링될때만 실행됨. 단, 처음 화면을 그릴 때 한번 실행됨
    // useEffect(() => {
    //     console.log(`age or name이 변경될 때 실행됩니다`);
    // }, [age, name]);

    useEffect(() => {
        console.log("name이 변경될 때 render 됩니다");
        // 컴포넌트가 unmount될 때 실행됩니다
        return () => {
            console.log(`unmount됩니다`); // 기존화면은 지우고, 리렌더링한다.
            console.log(`update전 값:${name}`); // state는 직전값이 나옵니다.
        }
    }, [name]);

    //★★useEffect는 여러개여도 됩니다

    return(
        <>
            이름:<input type="text" onChange={handleName}/><br/>
            나이:<input type="number" onChange={handleAge}/><br/>

            이름: {name}, 나이 {age}
        </>
    )
}

export default HookEffect;
import { useRef, useState } from "react";

const HookRef = () => {

    //사용자 입력값 data, 화면에 출력 값 result
    const [form, setForm] = useState({ data: '', result: '' })

    const handleChange = (e) => {
        setForm( {...form, ["data"]: e.target.value})
    }
    
    //등록
    const handleClick = () => {
        setForm( {data: '', result: form.data })

        //Ref의 사용
        // console.log(inputTag);
        // console.log(inputTag.current);
        // console.log(inputTag.current.value);

        inputTag.current.focus();
        
    }

    //특정 태그에 이름 달기 useRef()
    //반환된 이름을 사용할 태그에 ref속성에 넣습니다.
    //state로 관리할 필요 없고 직접 제어할 태그에 이름을 달고 current를 활용해서 핸들링 가능
    const inputTag = useRef();
    // console.log(inputTag);


    return (
        <>
            내용: <input type="text" onChange={handleChange} value={form.data} ref={inputTag}/>
            <button onClick={handleClick}>등록하기</button>
            <br/>
            결과: {form.result}
        </>
    )
}

export default HookRef;
//리듀서 선언(현재의 state, 업데이트에 필요한 정보)

import { useEffect, useReducer } from "react";

import {myReducer} from './HookReducerComponent'

// //action을 판단해서 state를 바꿔줌
// //state는 매개변수로 들어와서 매개변수로 나감
// const myReducer = (state, action) => {
//     //action은 객체
//     // console.log(state);
//     // console.log(action);
//     if(action.type === 'increase') {
//         state = {value : state.value + 1};
//     } else if(action.type === 'decrease') {
//         state = {value : state.value - 1};
//     } else if(action.type === 'reset') {
//         state = {value : 0};
//     }
//     /* {value: 0} 모형을 유지해주려고  return {value: state.value + 1};  */
//     return state; 
// }

const HookReducer = () => {
    /* 밖에 있는 reducer 사용*/
    //const [현재값, 리듀서를 업데이트 할 수 있는 함수] = useReducer(외부에서 사용할 리듀서 함수, 리듀서의 초기값)
   const [state, func]= useReducer(myReducer, {value: 0})
    
   /* useEffect(() => {
     //func가 대신 실행한다. 
       func({type: 'reset'}); //리듀서를 실행 시키고, myReducer의 action으로 전달 됩니다.
    },[])
    console.log(state); 
    */

    const up = () => {
        func({type: "increase"});
    }

    const down = () => {
        func({type: "decrease"});
    }

    return (
        <>
            <button onClick={up}>증가</button>
            <button onClick={down}>감소</button>
            <button onClick={() => func({type: "reset"})}>초기화</button>
            {/* state가 객체니까 state.value */}
            결과 {state.value}
        </>
    )
}

export default HookReducer;
import { useReducer } from "react";

import { nameReducer } from './HookReducerComponent';

// 리듀서
//  const nameReducer = (state, action) => {

//     console.log(action.name); //e.target.name

//    /*  if (action.name == "name") {
//         state = { ...state, ["name"]: action.value }
//     } else if (aciton.name == "age") {
//         state = { ...state, ["age"]: action.value }
//     } */

//     state = {...state, [action.name] : action.value };

//  return state;
//  }

const HookReducer2 = () => {

    /* state가 가지고 있는 값이 밑에가 됨 */
    //[스테이트, 리듀서 제어함수] = useReducer(리듀서, 초기값)
    const [state, func] = useReducer(nameReducer, { name: '', age: '' })

    const { name, age } = state; //스테이트 값 구조 분해 할당

    return (
        <>
            이름: <input type="text" name="name" onChange={(e) => func(e.target)} />
            나이: <input type="text" name="age" onChange={(e) => func(e.target)} />

            결과값: {name} <br />
            결과값: {age} <br />
        </>
    )
}

export default HookReducer2;
HookReducerComponent.js

//결론 리듀서를 이용하면 state값을 밖에서 관리할 수 있음
//action을 판단해서 state를 바꿔줌
//state는 매개변수로 들어와서 매개변수로 나감
const myReducer = (state, action) => {
    //action은 객체
    // console.log(state);
    // console.log(action);
    if(action.type === 'increase') {
        state = {value : state.value + 1};
    } else if(action.type === 'decrease') {
        state = {value : state.value - 1};
    } else if(action.type === 'reset') {
        state = {value : 0};
    }
    /* {value: 0} 모형을 유지해주려고  return {value: state.value + 1};  */
    return state; 
}

//리듀서
const nameReducer = (state, action) => {

    state = {...state, [action.name] : action.value };

    return state;
}

//기본 디폴트 모형(하나밖에 반환이 안됨)
export {myReducer, nameReducer};

 

* hookQ.js, hookQ2.js + 추가 주석 답안

import {  useRef ,useEffect, useState } from "react";

const HookQ = () => {

    /* id와 pw ref로 두고 직접 핸들링 할 수 있도록*/
    const input1 = useRef(null);
    const input2 = useRef(null);
    const [data, setData] = useState({ id: '', pw: '' });

    /* 딱 한번만 실행 됩니다. */
    useEffect(()=> {
        input1.current.focus(); //input1.current 실제 input 태그, 처음 마운트 이후 id 태그에 포커싱
    }, []);

    //인풋 태그의 핸들링
    const handleChange = (e) => {
        // setData({...data, ["id"]: e.target.value});
        // setData({...data, ["pw"]: e.target.value});
        setData({...data, [e.target.name]: e.target.value});
    }

    const handleClick = () => {
        if(input1.current.value === '') {
            input1.current.focus(); //id 태그
        } else if(input2.current.value === '') {
            input2.current.focus(); //pw 태그
        } else {
            alert(`${data.id} 이고 ${data.pw}`);
        }
    }

    /* 
    1. 컴포넌트가 마운트된 이후 한번만 id 태그에 포커스를 줍니다.
    2. id와 pw는 state로 관리합니다.
    로그인 버튼 클릭시 공백이라면 공백인 태그에 포커스를 주세요.
    로그인 버튼 클릭시 공백이 아니라면 경고창으로 id, pw를 출력해주세요.
     */
    return (
        <div>
            <h3>훅 실습</h3>
            <input type = "text" name ="id" placeholder="아이디" onChange={handleChange} ref={input1}/>
            <input type = "password" name ="pw" placeholder="비밀번호" onChange={handleChange} ref={input2}/>
            <button onClick={handleClick}>로그인</button>
        </div>
    )
}

export default HookQ;
import { useRef, useState, useEffect } from "react";


const HookQ2 = () => {

    /*
    실습(할일목록 만들기)
    1. state는 {todo: '', list; []}로 관리하세요
    2. 할일 목록을 작성하고 클릭 시, list에 인풋에 입력값을 추가하고 map을 통해서 화면을 그립니다.
    3. 등록버튼 클릭 이후에는 ref를 활용해서 input태그에 포커싱을 줍니다.
    */

    const inputRef = useRef(null); //input
    const [data, setData] = useState( {todo: '', list: []} );

    
    //인풋 데이터 핸들링
    const handleChange = (e) => {
        setData({...data, ["todo"]: e.target.value });
    }
    // console.log(data);
    
    //추가하기
    const handleClick = () => {
        const newList = data.list.concat( data.todo ); //기존 list는 유지하고 새롭게 합쳐진 리스트를 반환
        // console.log(newList);
        setData({list: newList, todo: ''});
        inputRef.current.focus(); //포커싱
    }

    // 화면을 그림
    const result = data.list.map((item, index) => <li key={index}>{index+1}. {item}</li> )

    return (
        <>
            <hr />
            <h3>ref로 할일목록 만들기</h3>
            <input type="text" name="todo" onChange={handleChange} placeholder="할일목록" value={data.todo} ref={inputRef}/>
            <button onClick={handleClick}>등록하기</button>
            <ul>
                {result}
            </ul>
        </>
    )


}

export default HookQ2;
import { useRef, useState, useEffect } from "react";


const HookQ2 = () => {

    /*
    실습(할일목록 만들기)
    1. state는 {todo: '', list; []}로 관리하세요
    2. 할일 목록을 작성하고 클릭 시, list에 인풋에 입력값을 추가하고 map을 통해서 화면을 그립니다.
    3. 등록버튼 클릭 이후에는 ref를 활용해서 input태그에 포커싱을 줍니다.
    */


    const todoTag = useRef();

    const [todoData, setTodoData] = useState({ todo: '', list: [] }); // 맨 처음에 초기값 세팅
    let newList = todoData.list.map((item, index) => <li key={index}>{item}</li>); // 배열에다가 li태그 붙여서 반환하고, 이 데이터를 화면에 뿌릴 것
    
    const handleTodoInput = (e) => { 
        setTodoData({ ...todoData, todo: e.target.value }); // 인풋값을 todo에 저장해둠
    }
    
    const todoRegister = () => { // 클릭이벤트
        if (todoTag.current.value === "") { // 값을 입력하지 않으면 실행하지 않도록 함
            todoTag.current.focus(); // 다만, 포커스를 줘서 바로 입력할 수 있게 해둠
            return;
        }
        let newArr = todoData.list.concat(todoData.todo); // todo에 저장한 input값을 list에 담아 newArr로 반환받는다
        setTodoData({ ...todoData, todo: '', list: newArr }); // 원래 todoData를 복사해오고, todo를 공백으로 바꾼다. 이미 list에 담았기 때문이다. 그리고 list에는 반환받은 newArr를 담는다.
        // 그 후 17줄로 돌아감. IterationComponent2와 동일한 원리
        todoTag.current.focus(); 
    }

    return (
        <>
            <hr />
            <h3>ref로 할일목록 만들기</h3>
            <input type="text" name="todo" placeholder="할일목록" ref={todoTag} onChange={handleTodoInput} value={todoData.todo} />
            <button onClick={todoRegister}>등록하기</button>
            <ul>
                {newList}
            </ul>
        </>
    )


}

export default HookQ2;

'TIL > React' 카테고리의 다른 글

day70-react  (0) 2023.01.19
day69-react  (0) 2023.01.18
day67-react  (0) 2023.01.17
day66-react  (0) 2023.01.16
day65-react  (0) 2023.01.13
profile

mjeongriver

@mjeongriver

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

검색 태그