form 태그에서 name이라는 이름으로 속성의 이름을 썼던 것처럼
props도 동일하게 동작
인자 전달부분에 {}를 추가하여 인자 전달부분에서 구조 분해할당으로 props를 받아와서 사용해도된다.
중간에 중괄호를 치면 문자 뿐마닝 아니라 변수 등으로 받아올 수도 있다.
props 활용하기
Props 로는 배열 같은 다양한 자료형의 전달이 가능합니다! props.arr.map
jsx의 장점: js과 HTML을 혼용할 수 있도록 해서 편한 사용가능
컴포넌트 꾸미기
JSX문법으로 객체 자체를 인라인에 선언해서
객체 형태에 각각의 속성을 넣는다. (camelCase로)
React에서 제공하는 문법이다.
Styled Components
컴포넌트 레벨에서 바로 스타일 지정이 가능하다. (공동작업시 바로 체크 가능)
PascalCase로 작성
각각의 태그를 변수에 할당하고 해당 태그의 실제적인 태그명을 styled 를 이용하여 지정합니다!
JS로 변형시켜서 구동되는 모듈
for문, if문도 지원한다.
조건부 렌더링
React는 컴포넌트 형식으로 구성된다.
이전에는 JS로 할 때는 클래스에 di-none 추가로 작동했었으나,
React는 조건식이나 특정변수를 앞에 두고 true/false일 때 보이게 안보이게 한다.
첫번째 컴포넌트와 두번째컴포넌트로도 사용가능하다.
useRef
변수 값을 설정후 이 값이 변화가 일어나도 리랜더링이 안일어나게 (DoM요소를 변화시키지 X)
ex. input값, 포커스 이동을 시킬 때
input을 쳐도 winputValue의 current. value에 저장된다.
input.current.focus
태초의 리액트에서는 클래스형 컴포넌트만 사용 (컴포넌트 별 상태 관리 및 라이프 사이클 기능을 지원)
메모리 문제 등 함수형으로 바뀜
기존 클래스형 컴포넌트에서 사용하던 편리한 기능(리액트의 핵심)을 함수형에 적용하려니 기존 것을 그대로 사용할 수는 없었고 새로운 것에 대한 필요성을 느낌
-> 그래서 탄생한 것이 React HOOKS 입니다
앞에 use 가 붙은 애들이 HOOKS 들이죠
useState / useRef / useEffect / useContext / useMemo / useCallback / useReducer
Fragment란 파편을 뜻한다.
컴포넌트를 생성해서 넣어보면, 의미없는 div가 3개가 겹치게된다.
root> app> div(컴포넌트로 사용하기 위한 부모용)> 실제 사용하는 html
리턴 값에서 최상위 태그 역할하는 div를 빼면, 에러가 뜬다.
React는 JSX Fragment를 추천.
<> </>
하지만 !!! 빈 태그 안에 속성값을 줘야할 때는 React.Fragment를 사용한다.
빈 태그에는 값을 못 주기 때문에 !!! (1. 배열을 만들어서 반환해야하는 키 값 또는 속성값을 전달해야하는 상황)
먼저 App.js 에 가서 ReactFragment 컴포넌트와 동일한 코드를 작성
& flex + spacebetween 적용시
ReactFragment 최상위 요소를 div로 변경해서 적용시
<>
를 써야한다. CSS문제가 생길 것 같다면 React.Fragment 사용
구조적으로 div가 분리가 될 경우
State 는 변경이 되면 바로 리렌더링이 일어나기 때문에 버튼을 클릭하면 바로바로 반영이 됩니다!
Ref 는 리렌더링이 일어나지 않기 때문에 버튼을 클릭하면 Console.log 에만 찍히다가 컴포넌트가 리렌더링이 되면 그 때 한꺼번에 반영 됩니다
그런데 왜 변수는 Console.log 에는 잘 찍히다가 렌더링이 되면 0 만 나올까요?
state는 하나하나 올라가고
Ref는 값을 유지하고 있다가 렌더링 됐을 때 한번에 변화
Variable은 리렌더링 되면 초기화가 된다.
(1) 무조건 실행 Mount
(2) 변화될 때 실행 Update
(3) 첫렌더링만 하고싶다면 Unmount
[]
- 빈 배열 [] 을 넣으면 최초 마운트 시에만 실행이 됩니다!
setText(inputValue.current.value) // useRef
// input 태그에 useRef를 저장하는 것에 쓰는건지 / 참조하는건지 잘 구분
// 저장할 때는 useRef 선언한 것. current ( 저장소로 쓴다면 초기화를 시켜야 한다.)
// 값을 참조할 때는 선언한 것.current.value로 한번 더 들어가야한다.
/ useRef
// input 태그에 useRef를 저장하는 것에 쓰는건지 / 참조하는건지 잘 구분
// 저장할 때는 useRef 선언한 것. current ( 저장소로 쓴다면 초기화를 시켜야 한다.)
// 값을 참조할 때는 선언한 것.current.value로 한번 더 들어가야한다
추가, ‘보이기‘ 버튼이 클릭 되면 버튼 이름을 ‘숨기기’로 변경 하기!
추가, 페이지가 처음 시작 되면 ‘보이기‘ 버튼에 포커스가 이동하도록 처리
import { useEffect, useRef, useState } from "react";
export default function PracticeTimer() {
const [ render, setRender ] = useState(0);
const time = useRef(0);
useEffect(()=> {
const timer = setInterval(() => {
time.current = time.current + 1; // Ref가 Interval 1초에 한번씩 값이 올라간다.
console.log(timer.current);
}, 1000); // 1000ms = 1초
return() => { // 위의 useEffect가 언마운트되면 실행되는 코드
clearInterval(timer);
console.log("타이머 종료");
}
},[]); // 켜질 때 한번만 실행되도록 빈 배열 켜기
const showTime = () => {
setRender(render + 1);
}
return (
// 3. 시간 버튼을 누르면 지금까지 마운트가 된 시간을 출력해 줍니다!
<>
<h1>{time.current}</h1>
<button onClick={showTime}>시간</button>
</>
)
}
App.js
// import './App.css';
import { useEffect, useRef, useState } from 'react';
import PracticeTimer from './components/PracticeTimer';
function App() {
const [show, setShow] = useState(false);
const changeFocus = useRef(); // 돔요소를 참조할 때는 데이터를 저장하려는 것이 아니기 때문에 useRef를 선언만 하면된다.(초기값 지정필요x)
useEffect(()=> {
changeFocus.current.focus(); // useEffect 안에 있기 때문에 한번만 될 것이다.
},[]);
return (
// 1. 버튼을 클릭하면 PracticeTimer 컴포넌트가 마운트 되고
<div className='App'>
{ show && <PracticeTimer/>}
<button ref={changeFocus} onClick={()=> setShow(!show)}>{show? "숨기기" : "보이기"}</button>
</div>
);
}
export default App;