[4주차 - 2] React.js 입문
State(=상태)
: 현재 가지고 있는 형태나 모양을 정의, 변화할 수 있는 동적인 값
컴포넌트의 현재 상태를 보관하는 변수
- state의 값에 따라 다른 UI를 렌더링
- state의 값의 변화를 감지하여 자동으로 컴포넌트 렌더링 (=리렌더, 리렌더링)
//App.jsx파일
import "./App.css";
import { useState } from "react";
function App() {
const [count, setState] = useState(0); // 비 구조화 할당 문법(=구조분해할당)사용
const [light, setLight] = useState("OFF");
// 버튼 클릭 시 state의 값이 1 증가(=클릭 시 리렌더링 발생)
return (
<>
<div>
<button onClick={() => {
setLight(light === "ON" ? "OFF" : "ON");
}}
>
{light === "ON" ? "끄기" : "켜기"}
</button>
</div>
<div>
<h1>{count}</h1>
<button
onClick={() => {
setState(count + 1);
}}
>
+
</button>
</div>
</>
);
};
export default App;
- useState의 첫번째 요소는 새롭게 생성된 state 값, 두번째 요소는 state의 값을 변경하는 함수(=상태변화함수)
- 클릭할 때마다 리렌더링 발생
컴포넌트 state값 변경>컴포넌트 리턴>화면 다시 렌더링>변경된 state의 값 화면에 반영
- 일반변수가 아닌 state를 사용해야 리렌더링이 발생함 (컴포넌트의 상태이기 때문)
State와 Props
/ 전구 컴포넌트 생성
const Bulb = ({light}) => {
return (
<div>
{light === "ON" ? (<h1 style={{backgroundColor: "orange" }}>ON</h1>)
: (<h1 style={{backgroundColor: "gray"}}>OFF</h1>)}
</div>
)
}
function App() {
const [count, setState] = useState(0);
const [light, setLight] = useState("OFF");
// 버튼 클릭 시 state의 값이 1 증가
return (
<>
<div>
<Bulb light={light} />
<button onClick={() => {
setLight(light === "ON" ? "OFF" : "ON");
}}
>
{light === "ON" ? "끄기" : "켜기"}
</button>
</div>
<div>
<h1>{count}</h1>
<button
onClick={() => {
setState(count + 1);
}}
>
+
</button>
</div>
</>
);
};
- Bulb를 자식 컴포넌트로 넣은 후 light 스테이트를 설정해준다
- state값이 변경되지 않아도 props가 변하면 리렌더링 된다(ON/OFF에 따라 바뀌는 페이지)
light의 state가 ON/OFF 상태로 있을 수 있고,
Bulb 컴포넌트에게 props(부모가 자식에게 넘겨주는 데이터)인 light값을 넘겨준다
리액트에서 리렌더링 되는 경우 3가지
1. state
2. props
3. 부모 컴포넌트
효율적 리렌더링을 위한 컴포넌트 분리
Bulb컴포넌트
const Bulb = () => {
const [light, setLight] = useState("OFF");
return (
<div>
{light === "ON" ? (<h1 style={{backgroundColor: "orange" }}>ON</h1>)
: (<h1 style={{backgroundColor: "gray"}}>OFF</h1>)}
<button onClick={() => {
setLight(light === "ON" ? "OFF" : "ON");
}}
>
{light === "ON" ? "끄기" : "켜기"}
</button>
</div>
)
}
Counter컴포넌트
const Counter=() => {
const [count, setState] = useState(0);
return (
<div>
<h1>{count}</h1>
<button
onClick={() => {
setState(count + 1);
}}
>
+
</button>
</div>
);
};
App
function App() {
return (
<>
<Bulb/ >
<Counter/ >
</>
);
};
- components 폴더 내에 컴포넌트명.jsx 파일을 생성하여 분리하는 편이 더 효율적이다
import/export문 잊지 않기
State로 사용자 입력 관리하기 - 간단한 회원가입 폼 만들기
Register.jsx 파일 생성
사용자의 정보를 state를 통해 저장
1. 이름 입력
// 간단한 회원가입 폼
import { useState } from "react";
const Register = () => {
const [name, setName] = useState("이름"); // "이름"을 초기값으로 설정
const onChangeName = (e) => {
setName(e.target.value); // value값 가져와서 name으로 설정
};
return (
<div>
<div>
<input
value={name}
onChange={onChangeName}
placeholder={"이름"}
/>
</div>
</div>
)
};
export default Register;
- placeholder: 인풋창에 작성가이드처럼 있는 투명글자
- 초기값 설정을 위해 value 속성도 설정한다
- console.log(e)로 value값을 알아내기
- 알아낸 value값을 setName의 매개변수로 넣어서 name값으로 지정한다
2. 날짜 입력
- div로 나눠서 날짜창을 줄바꿈을 한다
return (
<div>
<div>
<input
value={name}
onChange={onChangeName}
placeholder={"이름"}
/>
</div>
<div>
<input
value={birth}
onChange={onChangeBirth}
type="date"
/>
</div>
</div>
)
};
- name과 똑같이 useState, 이벤트핸들러 등을 만들면 된다
- 날짜 입력을 위한 코드 작성시 오류가 발생하지만 불가피하므로 넘어가준다
국가 선택
- state와 이벤트핸들러는 동일하게 작성
<div>
<select value={country} onChange={onChangeCouuntry}>
<option value={""}></option>
<option value={"kr"}>한국</option>
<option value={"us"}>미국</option>
<option value={"uk"}>영국</option>
</select>
</div>
- 셀렉트박스의 맨 처음 옵션이 초기값으로 설정된다
-빈옵션칸을 만들면 초기값으로 아무것도 설정되지 않는다
- 셀렉트박스의 선택지는 친절하게 설정, value값은 더 간단하게 따로 설정하는 것이 일반적이다
(value = "kr", option값은 한국)
자기소개 작성
<div>
<textarea value={""} onChange={onChangeBio}></textarea>
</div>
- textarea는 input과는 다르게 여러줄을 작성할 수 있다(내부동작은 동일)
더 효율적인 작성법
useState문이나 onChange가 반복되는 코드를 통합한다
const [input, setInput] = useState({
name: "",
birth: "",
country: "",
bio: "",
});
- 객체 형태를 통해 useState문을 통합
- 값들을 유지시키기 위해 ..input 스프레드 연산자를 통해 모든 값을 가져온 후 필요한 타겟값만 변경해준다
const onChange = (e) => {
setInput({
...input,
[e.target.value.name]: e.target.value,
});
<div>
<input
name="name"
value={input.name}
onChange={onChange}
placeholder={"이름"}
/>
</div>
<div>
<input
name="birth"
value={input.birth}
onChange={onChange}
type="date"
/>
</div>
<div>
<select
name="country"
value={input.country}
onChange={onChange}
>
<option value={""}></option>
<option value={"kr"}>한국</option>
<option value={"us"}>미국</option>
<option value={"uk"}>영국</option>
</select>
</div>
<div>
<textarea
name="bio"
value={input.bio}
onChange={onChange}></textarea>
</div>
</div>
- 이벤트 핸들러가 동작하는 state가 e.target.value.name가 되므로 동작에 따라 key값이 설정된다
(자기소개서를 작성하는 이벤트가 있으면 bio: e.target.value와 동일함)
useRef
const refObj = useRef(); // current: undefined 출력
- currnet라는 프로퍼티에 현재 보관값을 저장
클릭 수 ref의 current에 저장하여 알아보기
제출 시 이벤트 항목에 포커스 만들기
const inputRef = useRef(); // input 요소가 저장하는 DOM요소 저장됨
// 이벤트 핸들러 설정
const onSubmit = () => {
if(input.name === ""){
inputRef.current.focus();
}
}
<div>
<input
ref = {inputRef}
name="name"
value={input.name}
onChange={onChange}
placeholder={"이름"}
/>
</div>
<button onClick={onSubmit}>제출</button>
- 일반변수는 리렌더링시 초기화되지만, state와 useRef는 초기화되지 않는다
- - 만약 컴포넌트 외부에 일반변수를 둔다면 다른 컴포넌트를 공유하기 때문에 심각한 문제가 발생한다
React Hooks
: 클래스 컴포넌트의 기능을 함수 컴포넌트에서도 이용할 수 있도록 하는 메서드
- 과거 class 컴포넌트(기능많음)사용 시절 불편함을 해소하기 위해 function(기능적음) 컴포넌트 사용을 기대했고,
class컴포넌트 기능을 function 컴포넌트에서도 사용하기 위해 react hooks으로 기능을 가져왔다
그 결과 현재 function 컴포넌트만 사용한다
- 메서드 이름 앞에 use가 붙는다 (예: useSTate, useRef)
- 함수 컴포넌트, 훅 메서드 내부에서만 호출 가능
- 조건문, 반복문에서는 사용 불가
- 자신만의 react hook을 만들 수 있다 (앞에 use 사용 필요)
- hooks 폴더 내에 분리하는 것이 일반적이다