CSS파일
: 상단에 import ./파일명.css
: 이름충돌 유의
CSS모듈
: 상단에 import ./파일명.module.css 객체형식으로 받아서 속성으로 입력하면 됌
: 이름충돌 문제 없음
Sass
: npm install node-sass 설치
: 파일형식은 .scss
css-in-js
: js파일 안에서 css를 사용하는 방법
: npm install styled-components 설치
단일 페이지 애플리케이션(SPA)
: 클라이언트가 초기요청 서버가 html을 내려주고 페이지 전환이 있을땐, 필요할 때마다 데이터를 요청해서 받아오고 라우팅 처리로 가능
SPA 브라우저 API
: pushState, replaceState함수
: popState함수
- SPA 브라우저 만들기
=> App.js
function App(){
const [pageName, setPageName] = useState('');
useEffect(()=>{
window.onpopstate = function (event) {
console.log(`location: ${document.location}, state: ${event.state}`);
setPageName(event.state);
};
}, []);
function onClick1(){
const pageName = 'page1';
window.history.pushState(pageName,'','/page1');
setPageName(pageName);
}
function onClick2(){
const pageName = 'page2';
window.history.pushState(pageName,'','/page2');
setPageName(pageName);
}
return (
<div>
<button onClick={onClick1}>
page1
</button>
<button onClick={onClick2}>
page1
</button>
{!pageName && <Home/>}
{pageName === 'page1' && <Page1/>}
{pageName === 'page2' && <Page2/>}
</div>
);
}
function Home(){
return <h2> 여기는 홈페이지입니다. </h2>
}
function Page1(){
return <h2> 여기는 Page1입니다.</h2>
}
function Page2(){
return <h2> 여기는 Page2입니다.</h2>
}
- SPA로 동작하는 웹사이트 만들기
: npm install react-router-dom 설치
: route태그를 통해서 3가지 component가 렌더링됌
: exact부분은 url형식을 지정해줌
=> App.js
import logo from './logo.svg';
import './App.css';
import React, {useState , useEffect} from 'react';
import reactDom from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { Route, Link } from 'react-router-dom';
import Rooms from './Rooms'
function App(){
return (
<BrowserRouter>
<div style={{padding: 20, border: '5px solid gray '}}>
<Link to="/">홈</Link>
<br/>
<Link to="/photo">사진</Link>
<br/>
<Link to="/rooms">방 소개</Link>
<br/>
<Route exact path="/" component={Home}/>
<Route exact path="/photo" component={Photo}/>
<Route exact path="/rooms" component={Rooms}/>
</div>
</BrowserRouter>
);
}
function Home(){
return <h2> 여기는 홈페이지입니다. </h2>
}
function Photo(){
return <h2> 여기서 사진을 감상해보세요.</h2>
}
export default App;
=> Rooms.js
import React from 'react';
import { Route, Link } from 'react-router-dom';
export default function Rooms({match}){
return(
<div>
<h2> 여기는 방을 소개하는 페이지. </h2>
<Link to={`${match.url}/bludRoom`}>파란 방입니다.</Link>
<br/>
<Link to={`${match.url}/greenRoom`}>초록 방입니다.</Link>
<br/>
<Route path={`${match.url}/:roomId`} component={Room}/>
<Route
exact
path={match.url}
render={()=><h3>방을 선택해 주세요.</h3>}
/>
<br/>
</div>
);
}
function Room({match}){
return <h2>{`${match.params.roomId} 방을 선택했습니다.`}</h2>
}
: [...arr]은 array의 모든 요소를 나열해주는 것과 같음
: 리액트는 데이터를 기반으로 UI가 어떤 모습인지를 기술해줌
: UI의 모습이 한눈에 그려짐(추상화 단계가 높아 비즈니스 로직에 더 집중할 수 있음)
import logo from './logo.svg';
import './App.css';
import React, {useState , useEffect} from 'react';
import reactDom from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { Route, Link } from 'react-router-dom';
import Rooms from './Rooms'
import { current } from 'immer';
export default function App(){
const [todoList, setTodoList] = useState([]);
const [currentId, setCurrentId] = useState(1);
const [desc, setDesc] = useState('');
const [showOdd, setShowOdd] = useState(false);
function onAdd(){
const todo = { id: currentId, desc };
setCurrentId(currentId+1);
setTodoList([...todoList, todo]);
}
function onDelete(e){
const id = Number(e.target.dataset.id);
const newTodoList = todoList.filter(todo => todo.id !== id)
setTodoList(newTodoList);
}
function onSaveToServer(){
}
return (
<div>
<h3>할 일 목록</h3>
<ul>
{todoList.filter((_,index)=> showOdd ? index%2 == 0: true ).map(todo => (
<li key={todo.id}>
<span>{todo.desc}</span>
<button data-id={todo.id} onClick={onDelete}>
삭제
</button>
</li>
))}
<input type="text" value={desc} onChange={e=>{setDesc(e.target.value)}}/>
<button onClick={onAdd}>추가</button>
<button onClick={()=> setShowOdd(!showOdd)}>
홀수 아이템만 보기 on/off
</button>
<button onClick={onSaveToServer}>서버에 저장</button>
</ul>
</div>
);
}
UI컴포넌트에서 데이터 다루는 법
=>App.js
import logo from './logo.svg';
import './App.css';
import React, {useState , useEffect} from 'react';
import reactDom from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { Route, Link } from 'react-router-dom';
import Rooms from './Rooms'
import { current } from 'immer';
import Counter from './Counter';
export default function App(){
const [color, setColor] = useState('red');
function onClick(){
setColor('blue');
}
return(
<div>
<Counter/>
<Counter/>
<button style={{backgroundColor:color}} onClick={onClick}>
좋아요
</button>
</div>
);
}
=>Title.js
import React, { memo } from 'react';
function Title({title}){
return <p> {title} </p>
}
export default React.memo(Title);
=>Counter.js
import React, {useState} from 'react';
import Title from './Title';
export default function Counter() {
const [count, setCount] = useState({value:0,value1:1,value2:2});
function onClick(){
setCount({...count, value:count.value+1});
}
return (
<div>
<Title title={`현재 카운트: ${count.value}`}/>
<button onClick={onClick}>증가</button>
</div>
);
}
return(
<React.Fragment>
<p>안녕</p>
<p>하세요.</p>
</React.Fragment>
);
하나의 화면을 표현하기 위해서 여러 리액트 요소가 트리구조로 구성
화면 구성은 "랜더단계 -> 커밋단계" 를 거침
(렌더단계: 가상돔 / 커밋단계: 실제돔)
리액트 훅(Hook)은 컴포넌트에 기능을 추가할 때 사용하는 함수
컴포넌트에 상태값을 추가, 자식요소에 접근과 같은 기능을 사용하고 싶을 때 훅을 사용
훅이 나오면서 클래스형 컴포넌트는 더이상 사용안해도 되는 상황
useState
비동기, 배치로 처리
여러상태값을 관리할 때는 useState보다는 useReducer가 더 적합
useEffect
렌더링 결과가 실제 돔에 반영되고 비동기로 호출이 됌
*외부함수는 의존성 배열에 입력할 필요없지만, 지역함수는 부수효과 내부에서 사용했다면 의존성 배열에 입력해줘야 됌.
=> App.js
import logo from './logo.svg';
import './App.css';
import React, {useState , useEffect} from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { Route, Link } from 'react-router-dom';
import Rooms from './Rooms'
import { current } from 'immer';
import Counter from './Counter';
console.log(
<a key="key1" style={{width:100}} href="http://google.com">
click here
</a>,
)
export default function App(){
const [count, setCount ] = useState(0);
function onClick() {
ReactDOM.unstable_batchedUpdates(()=>{
setCount(v => v+1);
setCount(v => v+1);
});
}
useEffect(()=>{
window.addEventListener('click',onClick);
return ()=>window.removeEventListener('click',onClick);
});
console.log('render canceled');
return(
<div>
<h2>{count}</h2>
<button onClick={onClick}>증가</button>
</div>
);
}
훅은 재사용성이 좋음
커스텀 훅 만들어보기!
=> App.js
import logo from './logo.svg';
import './App.css';
import React from 'react';
import Profile from './Profile';
export default function App(){
return <Profile/>;
}
=> Profile.js
import React,{useState, useEffect} from 'react';
import useUser from './useUser';
export default function Profile({userId}){
const user = useUser(userId);
return(
<div>
{!user&&<p>사용자 정보를 가져오는 중...</p>}
{user&&(
<>
<p>{`name is ${user.name}`}</p>
<p>{`age is ${user.age}`}</p>
</>
)}
</div>
);
}
=> useUser.js
import React,{useState, useEffect} from 'react';
export default function useUser(userId){
const [user, setUser] = useState(null);
useEffect(()=>{
getUserApi(userId).then(data=>setUser(data));
}, [userId]);
return user;
}
const USER1 = { name: "mike", age: 23 };
const USER2 = { name: "jane", age: 41 };
function getUserApi(userId){
return new Promise(res => {
setTimeout(()=>{
res(userId % 2 ? USER1 : USER2);
}, 500);
}
);
}
import React,{useState, useEffect} from 'react';
export default function useWindowWidth(){
const [mounted, setMounted] = useState(false);
useEffect(()=>{
setMounted(true);
}, []);
return mounted
}