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 |