티스토리 뷰
immer를 사용하여 더 쉽게 불변성 유지하기
yarn add immer
기본 코드
import produce from 'immer';
cosnt nextState=produce(originalState,draft=>{
//바꾸고 싶은 값 바꾸기
draft.somewhere.deep.inside=5;
})
//produce의 첫번째인자를 함수로 넣어도 돼 아래처럼
cosnt nextState=produce(draft=>{
//바꾸고 싶은 값 바꾸기
draft.somewhere.deep.inside=5;
})
ex
import React, { useRef, useCallback, useState } from "react";
import produce from "immer";
const App = () => {
const nextId = useRef(1);
const [form, setForm] = useState({ name: "", username: "" });
const [data, setData] = useState({
array: [],
uselessValue: null,
});
const onChange = useCallback(
(e) => {
const { name, value } = e.target;
setForm(
produce((draft) => {
draft[name] = value;
})
);
// setForm(
// produce(form, (draft) => {
// draft[name] = value;
// })
// );
// setForm({
// ...form,
// [name]: [value],
// });
},
[]
// [form]
);
const onSubmit = useCallback(
(e) => {
e.preventDefault();
const info = {
id: nextId.current,
name: form.name,
username: form.username,
};
setData(
produce((draft) => {
draft.array.push(info);
})
);
// setData(
// produce(data, (draft) => {
// draft.array.push(info);
// })
// );
// setData({
// ...data,
// array: data.array.concat(info),
// });
setForm({
name: "",
username: "",
});
nextId.current += 1;
},
[form.name, form.username]
// [data, form.name, form.username]
);
const onRemove = useCallback(
(id) => {
setData(
produce((draft) => {
draft.array.splice(
draft.array.findIndex((info) => info.id === id),
1
);
})
);
// setData(
// produce(data, (draft) => {
// draft.array.splice(
// draft.array.findIndex((info) => info.id === id),
// 1
// );
// })
// );
// setData({
// ...data,
// array: data.array.filter((info) => info.id !== id),
// });
},
[]
// [data]
);
return (
<div>
<form onSubmit={onSubmit}>
<input
name="username"
placeholder="아이디"
value={form.username}
onChange={onChange}
/>
<input
name="name"
placeholder="이름"
value={form.name}
onChange={onChange}
/>
<button type="submit">등록</button>
</form>
<div>
<ul>
{data.array.map((info) => (
<li key={info.id} onClick={() => onRemove(info.id)}>
{info.username} ({info.name})
</li>
))}
</ul>
</div>
</div>
);
};
export default App;
SPA(Single Page Application) : 한 개의 페이지로 이루어진 애플리케이션
SPA의 경우 서버에서 사용자에게 제공하는 페이지는 한 종류이지만 ,해당 페이지에서 로딩된 자바스크리브와 현재 사용자 브라우저의 주소 상태에 따라 다양한 화면을 보여 줄 수 있다.
다른 주소에 다른 화면을 보여 주는 것을 라우팅이라고 한다. 리액트 라우팅 라이브러리는 리액트 라우터, 리치 라우터, Next.js 등 여러가지가 있다.
SPA의 단점 :
1. 앱의 규모가 커지면 자스 파일이 너무 커진다는 것.
=> 코드 스플리팅을 사용하면 라우트별로 파일들을 나누어서 트래픽과 로딩 속도를 개선할 수 있다.
2. 브라우저에서 자바스크립트를 사용하여 라우팅을 관리하는 것은 자바스크립트를 실행하지 않는 일반 크롤러에서는 페이지의 정보를 제대로 수집해 가지 못한다는 잠재적인 단점이 따른다. 그렇기 때문에 검색 엔진의 검색 결과에 페이지가 나타나지 않을 수도 있다.(구글제외) 또한 자바스크립트 파일이 로딩되어 실행되는 짧은 시간 동안 흰 페이지가 나타날 수 있다는 단점도 있다.
=> 서버 사이드 렌더링을 통해 해결
yarn add react-router-dom
BrowserRouter는 웹 애플리케이션에 HTML5의 History API를 사용하여 페이지를 새로고침하지 않고도 주소를 변경하고, 현재 주소에 관련된 정보를 props로 쉽게 조회하거나 사용할 수 있도록 해준다.
Route : 어떤 규칙을 가진 경로에 어떤 컴포넌트를 보여 줄지 정의할 수 있다.
Link : a와 비슷한데 새로고침안됨
withRouter : 라우트로 사용된 컴포너늩가 아니어도 match, location, history 객체를 접근할 수 있게 해준다.
swtich 컴포넌트는 여러 Route를 감싸서 그중 일치하는 단 하나의 라우트만을 렌더링시켜 준다.
NavLink는 Link와 비슷하다. 현재 경로와 Link에서 사용하는 경로가 일치하는 경우 특정 스타일 혹은 CSS 클래스를 적용할 수 있는 컴폰넌트이다. 링크가 활성화되었을 때의 스타일을 적용할 때는 activeStyle 값을, CSS 클래스를 적용할 때는 activeClassName 값을 props로 넣어주면 된다.
exact true면 정확히 "~~"경로에서만 컴포넌트를 보여줌
여러 경로에서 같은 컴포넌트를 보여주는 방법
<Route path="/" component={Home} exact={true} />
<Route path={['/about', '/info']} component={About} />
1. 피라미터 : /profile/velopert
2. 쿼리 : /about?details=true
1. match.params
/profile/:username
2. location형태
{
"pathname":"/about",
"search":"?detail=true",
"hash":""
}
쿼리 문자열을 객체로 변환할 때는 qs 라이브러리 사용. yarn add qs
import React from "react";
import qs from "qs";
const About = ({ location }) => {
const query = qs.parse(location.search, {
ignoreQueryPrefix: true, //이 설정을 통해 문자열 맨 앞의 ?를 생략합니다.
});
const showDetail = query.detail === "true"; // 쿼리의 파싱 결과 같은 문자열입니다.
return (
<div>
<h1>소개</h1>
<p>이 프로젝트는 리액트 라우터 기초를 실습해 보는 예제 프로젝트입니다.</p>
{showDetail && <p>detail 값을 true로 설정하셨군요!</p>}
</div>
);
};
export default About;
뒤로가기 막아버려
componentDidMount() {
//이것을 설정하고 나면 페이지에 변화가 생기려고 할 때마다 정말 나갈 것인지를 질문함
this.unblock = this.props.history.block("정말 떠나실 건가요?");
}
componentWillUnmount() {
//컴포넌트가 언마운트되면 질문을 멈춤
if (this.unblock) {
this.unblock();
}
}
useEffect(() => {
console.log(history);
const unblock = history.block('정말 떠나실건가요?');
return () => {
unblock();
};
}, [history]);
swtich, 존재하지 않는 페이지 설정
<Switch>
<Route path="/" component={Home} exact={true} />
<Route path={["/about", "/info"]} component={About} />
<Route path="/profiles" component={Profiles} />
<Route path="/history" component={HistorySample} />
<Route
//path를 따로 정의하지 않으면 모든 상황에 랜더링됨
render={({ location }) => (
<div>
<h2>이 페이지는 존재하지 않습니다:</h2>
<p>{location.pathname}</p>
</div>
)}
/>
</Switch>
'책 > 리다기' 카테고리의 다른 글
리다기 정리7(context) (0) | 2021.01.24 |
---|---|
리다기 정리6(뉴스뷰어 만들기) (0) | 2021.01.22 |
리다기 정리4(아이콘, 최적화) (0) | 2021.01.21 |
리다기 정리3(css) (0) | 2021.01.20 |
리다기 정리2(hook) (0) | 2021.01.19 |