context를 이용하면 단계마다 일일이 props를 넘겨주지 않고도 컴포넌트 트리 전체에 데이터를 제공할 수 있다.
일반적인 React 데이터는 위에서 아래로 즉 부모에서 자식으로 props를 통해 전달된다.
하지만 여러 컴포넌트에게 전달해줘야한다면 props drilling과 같은 컴포넌트를 지나 또 props를 넘겨줘야 한다.
컴포넌트가 한 두 개 정도이면 크게 불편함도 없지만, 만약 컴포넌트가 3~4개 혹은 그 이상일 경우에는 props를 넘겨주는 것만으로도 굉장히 번거로워 질 수 있다.
하지만 context를 이용하면 많은 컴포넌트가 공통으로 state를 공유 할 수 있게 된다.
context는 리액트에서 제공하는 createContext를 사용해서 생성할 수 있다.
import { createContex } from 'react';
export const GlobalContext = createContext({
name: "",
setName: (_) => {}
})
context 데이터를 사용할 변수에 createContex 함수의 인자값으로 관리할 state 들을 객체 형태로 담아준다.
state를 설정했으니 state 값을 변경할 수 있는 set함수도 함께 설정할 수 있다.
위와 같이 만들어도 아직 state의 값이 만들어진 것은 아니다.
useState를 이용해 설정한 state 값들을 만들어줘야 한다.
function MyApp({ Component, pageProps }) {
const [name, setName] = useState("anyName");
설정한 state 값들은 useState로 생성하면 된다.
그럼 이제 하위 컴포넌트로 뿌려주기 위한 작업을 해주면 된다.
app.js 또는 app.tsx가 페이지를 그려내는 부분이기 때문에 return 해주는 곳에 전체 페이지들을 context로 감싸준다.
return (
<GlobalContext.Provider value={{
name, setName
}}>
<ApolloProvider client={client}>
<Layout>
<Global styles={globalStyles} />
<Component {...pageProps} />
</Layout>
</ApolloProvider>
</GlobalContext.Provider>
위 코드와 같이 GlobalContext
로 감싸줄때 Provider
기능을 사용할 수 있다.
Provider
기능은 value
속성으로 전체 페이지에 state
값을 넣어줄 수 있다.
이제부터는 모든 컴포넌트에서 Provider
로 전달된 state
의 값들을 가지게 된다.
import {useContext} from "react"
import {GlobalContext} from "/.app"
export default function ContextTestPage() {
const {name, setName} = useContext(GlobalContext);
return(
<div>이름: {name}</div> // 이름: anyname
)
}
이제 컴포넌트에서는 위와 같이 props
를 통해 state
를 넘겨받지 않아도 공통스테이트를 관리하는 컴포넌트에서 바로 불러와서 사용할 수 있게 된다.
하지만 context를 사용하면 컴포넌트의 재사용성이 떨어지므로 너무 남발하지 않는게 좋다.
위에서 설명한 context-api에는 한가지 단점이 있다.
Provider
로 감싸고 있는 부분이 업데이트가 되지 않은 state에도 리렌더가 일어나게 된다.
하지만 recoil은 이와 같은 context의 단점을 보완하여 나왔다.
recoil은 업데이트된 state 부분만 리렌더를 실행하게 되는데, 하지만 같은 state를 가지고 있다면 그 부분 또한 리렌더가 된다.
이러한 부분 때문에 context 보다 불필요한 렌더링이 줄어들게 된다.
사용법 또한 context에 비해 조금더 간단하다.
먼저 터미널에 yarn add recoil
을 입력한다.
npm을 사용중이라면 install
을 해주면 된다.
//app.tsx 파일
import {
RecoilRoot,
atom,
selector,
useRecoilState,
useRecoilValue,
} from 'recoil';
function App() {
return (
<RecoilRoot>
// RecoilRoot로 모든 컴포넌트를 묶어줌
<Component />
</RecoilRoot>
);
}
위와 같이 app 파일에 세팅을 해주면 준비는 끝난다.
// Atom
import {atom} from 'recoil'
const textState = atom({
key: 'textState', // state의 이름
default: '', //초기값
});
recoil에서는 state를 atom으로 참조를 하게 되고 위에서 말한 같은 부분을 리렌더 해준다는 것은 같은 atom을 참조하고 있는 state를 말하는 것이다.
즉 atom을 참조하고 있는 state를 가지고 있는 컴포넌트는 모두 리렌더링이 일어나게 되는 것이다.
// TextInput 컴포넌트
function TextInput() {
const [text, setText] = useRecoilState(textState);
const onChange = (event) => {
setText(event.target.value);
};
return (
<div>
<input type="text" value={text} onChange={onChange} />
<br />
Echo: {text}
</div>
);
}
atom을 불러온 컴포넌트에서 사용하는 방법 또한 간단하다.
recoil은 useState
를 사용하지 않고, 위와 같이 useRecoilState
를 사용하게 된다.
그 부분 이외에는 useState
와 다른 부분은 없다.
이제는 props를 사용하지 않고 같은 state
를 공유할 수 있게 된다.
리덕스(redux)는 자바스크립트의 상태관리 라이브러리이다.
리덕스의 기본 개념
setState
를 활용해 상태 변경을 하는데 리덕스에서는 액션이라는 객체를 통해 상태를 변경한다.액션(Action)은 앱에서 스토어에 운반할 데이터를 말하고 자바스크립의 객체 형식으로 되어있다.
{
type: 'ACTION_CHANGE_USER', // 필수
payload: { // 옵션
name: '철수',
age: 13
}
}
dispatch()
메소드를 사용해야 한다.리덕스의 데이터 흐름은 단방향이다.
그렇기에 아래와 같은 흐름을 가진다.
dispatch()
메소드에 전달된다.dipatch(Action)
를 통해 Reducer를 호출한다.리덕스는 추가적인 라이브러리 설치로 인해 프로젝트가 무거워진다.
또한 사용방법도 리코일에 비해 복잡한 부분이 있다.
Mobx 또한 상태 관리 라이브러리들 중 하나이다.
Mobx의 특징
Mobx를 사용하는 이유
SWR은 데이터를 가져오는 리액트의 훅스이다.
SWR은 먼저 개시로부터 데이터를 반환한 후 fetch 요청을 하고 최종적으로 데이터를 가져온다.
SWR을 사용하면 컴포넌트는 지속적이며 자동으로 데이터 업데이트 스트림을 받게 된다.
import useSWR from 'swr'
function Profile() {
const { data, error } = useSWR('/api/user', fetcher)
if (error) return <div>failed to load</div>
if (!data) return <div>loading...</div>
return <div>hello {data.name}!</div>
}
위 예시에서 useSWR
hook은 문자열로 된 키와 함수를 받는다.
키는 고유한 식별자 이다.
그리고 함수는 데이터를 반환하는 어떠한 비동기 함수도 될 수 있다.
훅은 요청의 상태에 기반한 데이터와 에러 두 개의 값을 반환한다.
SWR은 단 한줄의 코드로 프로젝트 내에 데이터를 가져오는 로직을 단순화 할 수 있다.