리액트에서 일반적인 값을 자손에게 전달하는 방법은 하향식으로 전달해야한다.
전달 받은 값을 자손에서 부모에게 변경하고 싶다고 알려주려면, 하향식으로 전달받은 함수를 이용해야 한다.
Level이 깊어질수록 불편함을 야기한다.
이를 해결하기 위한 방법으로 flux, reflux, redux, mobx 를 이용할 수 있다.
React 16.3부터는 Context API
를 이용해서 트리의 모든 레벨에 값을 공유하는 방법을 제공한다.
Context
컴포넌트와 멀리 떨어져있는 data저장공간
컴포넌트에서는 원할때마다 Context
에서 data를 가져와 사용할 수 있다.
React.createContext
Context 객체를 만들 수 있다.
const MyContext = createContext(defaultValue)
Context.Provider
Provider를 이용하여 Context 변경 사항을 자손들에게 제공할 수 있다.
<MyContext.Provider value={value}></MyContext.Provider>
Provider의 Value는 하위의 모든 Consumer에서 사용할 수 있으며, Provider 하위의 모든 Consumer는 Provider의 value가 변경될때마다 재렌더링 된다.
Context.Consumer
Class.contextType
useContext
Hook의 useContext로 Context 객체의 value를 가져올 수 있다.
const value = useContext(MyContext);
const UserContext = React.createContext()
const UserContextProvider = ({children}) => (
<UserContext.Provider>{children}</UserContext.Provider>
)
UserContextProvider
를 집어넣는 모든 컴포넌트는 children태그 안에서 유지된다.
<UserContext.Provider value={{ name:'Nico' }}>
{children}
</UserContext.Provider>
Provider에 value값을 넣어주면, Provider의 모든 children들은 value에 대한 접근 권한이 생긴다.
먼저 App 컴포넌트에 들어있는 모든 것을 ContextProvider 내부에 두어야한다.
function App() {
return (
<UserContextProvider>
<Screen />
</UserContextProvider>
);
}
이제 ContextProvider
내부의 <Screen />
컴포넌트가 Provider의 children이 될 것이다.
header
import React, { useContext } from 'react';
import { UserContext } from './context';
const Header = () => {
const {name} = useContext(UserContext);
return (
<header>
<a href="#">Home</a> Hello, {name}
</header>
)
}
user value가 필요한 컴포넌트에 간단하게 UserContext를 불러와서 복잡한 과정없이 사용
const UserContextProvider = ({children}) =>{
const [user,setUser] = useState({
name: 'Nico',
loggedIn: false
})
const logUserIn = () => setUser({...user, loggedIn: true })
return (
<UserContext.Provider value={{ name:'Nico' }}>
{children}
</UserContext.Provider>
)
}
여기서 setUser({...user, loggedIn: true })
의 ...user
는 loggedIn
외의 다른 모든 Object를 뜻한다.
만약 ...user
를 사용하지 않는다면, setUser는 내가 가지고 있는 state값을 모두 loggedIn값으로 대체해버리게되고, name값이 없어지게 된다.
header
const Header = () => {
const {user:{name,loggedIn}} = useContext(UserContext);
return (
<header>
<a href="#">Home</a> Hello, {name},
You are { loggedIn ? 'logged in' : 'anonymous' }{" "}
</header>
)
}
Screen.js
const Screen = () => {
const {logUserIn} = useContext(UserContext);
return (
<div>
<Header />
<h1>First Screen</h1>
<button onClick={logUserIn}>Log user in</button>
</div>
)
}
다른 컴포넌트에서 UserContext의 함수를 이용해 user의 상태를 변경해줄 수 있다.
위 코드를 보면 useContext(UserContext)
처럼 반복되는 코드가 많다.
UserContext
export const useUser = () => {
const {user} = useContext(UserContext);
return user
}
따로 user관리해주는 함수를 만든뒤 useContext를 초기화해주는 작업을 진행하고,
header
const Header = () => {
const {name, loggedIn} = useUser()
//...
}
useUser()
함수만 import해와서 사용한다면 불필요한 코드가 훨씬 줄어들고 간결해진다.
리액트에서 컴포넌트의 상태관리를 위해서 기본적으로 가장 많이 쓰이는 hook은 setState()
함수지만
좀 더 복잡한 상태관리가 필요한 React 컴포넌트에서는 useReducer()
hook함수를 사용한다.
기본적으로 useReducer()
hook함수는 다음과 같은 형태로 사용
const [<상태 객체>, dispatch] = useReducer(reducer,<초기상태>,<초기함수>)
useReducer
의 두번째 인자는 state의 초기값이다.
reducer함수는 두가지를 인자로 받는다.
reducer function에서 return 하는 object는 state를 '대체'할 object이다.
const [state,dispatch] = useReducer(reducer, { count : 0 });
여기서는 reducer에 첫 action을 보낼때, 여기있는 count(초기값)부터 시작한다는 뜻
action
은 (이름상관없음. potato라고 칭해도 된다) reducer에 정보를 주기위한, reducer와 대화하기 위한 변수이다.
useReducer
는 state와 dispatch가 제공된다 (useState에서 state와 setState가 제공되는 것처럼)
dispatch가 하는 일은 reducer에 action을 보내주는 것이다.
엄밀히 말하면, action을 보내주는 것은 아니고, action을 가지고 reducer function을 다시 실행하는 것이다.
Reducer function이 어떠한 값을 리턴하더라도 리턴한 값이 state를 '대체'한다. 그 값은 컴포넌트의 state값이 된다.
예를들어,
const reducer = (state,action) => ({ count:9999 })
리듀서 함수를 위와 같이 리턴했을때 stat값은 count:9999로 적용된다.
긴 랜덤 숫자를 만들어주는 모듈
$ npm i uuid
import uuid from 'uuid/v4'
💢 최신 버전은 강의처럼 import를 해오면 에러나서 아래와 같이 바꿔줌
import { v4 as uuid } from 'uuid'
// 사용법
{ id : uuid() }
state.completed.length !== 0 && <>...</>
length가 undefined라는 오류를 출력
reducer function에 previous state값을 포함하지 않았기 때문에 발생하는 오류
return { ...state, toDos : [...state.toDos, {text: action.payload, id : uuid()}] }
state값을 포함시켜줬더니 에러 해결
항상 이전의 state값을 return해줘야한다.