Context
- context를 이용하면 단계마다 일일이 props를 넘겨주지 않고도 컴포넌트 트리 전체에 데이터를 제공할 수 있다.
- context는 React 컴포넌트 트리 안에서 전역적(global)이라고 볼 수 있는 데이터를 공유할 수 있도록 고안된 방법이다.
그러한 데이터로는 현재 로그인한 유저, 테마, 선호하는 언어 등이 있다.
- React 애플리케이션에서 데이터는 props를 통해서 부모에서 자식에게 전달되지만,
애플리케이션 안의 여러 컴포넌트들에게 props를 전달해줘야 하는 경우 context를 이용하면
명시적으로 props를 넘겨주지 않아도 값을 공유할 수 있게 해주는 것이다.
=> 데이터가 필요할 때마다 props를 통해 전달할 필요가 없이 context를 이용해 공유한다.
context API를 사용하기 위해서는 Provider, Consumer, createContext가 필요하다.
① createContext : context 객체를 생성한다.
createContext 함수 호출 시 Provider와 Consumer 컴포넌트 반환한다.
initialValue는 Provider를 사용하지 않았을 때 적용될 초기값을 의미한다.
② Provider : 생성한 context를 하위 컴포넌트에게 전달하는 역할을 한다.
③ Consumer : context의 변화를 감시하는 컴포넌트이다.
설정한 상태를 불러올 때 사용한다.
useContext
- useContext 를 사용하면 기존의 Context 사용 방식보다 더 쉽고 간단하게 Context를 사용이 가능하고, 앞서 다뤘던 useState, useEffect와 조합해서 사용하기 쉽다는 장점이 있다.
- useContext를 사용할 때 주의해야 할 점은 Provider에서 제공한 value가 달라진다면 useContext를 사용하고 있는 모든 컴포넌트가 리렌더링 된다는 점이다. 따라서 useContext를 사용할 때 value 부분을 메모제이션 하는데 신경을 써야한다.
npm install react-router-dom / yarn add react-router-dom
npm install axios / yarn add axios
day09
cd day09
npm install react-router-dom
npm install axios
npm start
day09
src
components
Count.jsx
contexts
CountContext.jsx
App.js
import React from 'react';
import Count from './components/Count';
const App = () => {
return (
<div>
<Count/>
</div>
);
};
export default App;
Count.jsx
import React from 'react';
const Count = () => {
return (
<div>
<h1>카운트 : { count }</h1>
<p>
<button>증가</button>
<button>감소</button>
</p>
</div>
);
};
export default Count;
context API를 사용하기 위해서는 Provider, Consumer, createContext가 필요하다.
① createContext : context 객체를 생성한다.
createContext 함수 호출 시 Provider와 Consumer 컴포넌트 반환한다.
initialValue는 Provider를 사용하지 않았을 때 적용될 초기값을 의미한다.
② Provider : 생성한 context를 하위 컴포넌트에게 전달하는 역할을 한다. -- App.js에서 자식들한테 전달한다는 말
③ Consumer : context의 변화를 감시하는 컴포넌트이다.
설정한 상태를 불러올 때 사용한다.
Context를 사용할 컴포넌트를 상위 컴포넌트에서 Provider로 감싸주어야 한다.
Provide의 모든 하위 컴포넌트가 얼마나 깊이 위치에 있는지 상관없이 Context의 데이터를 읽을 수 있다.
App.js
import React from 'react';
import Count from './components/Count';
const App = () => {
return (
<div>
{/*
Context를 사용할 컴포넌트를 상위 컴포넌트에서 Provider로 감싸주어야 한다.
Provide의 모든 하위 컴포넌트가 얼마나 깊이 위치에 있는지 상관없이 Context의 데이터를 읽을 수 있다.
*/}
<CountProvider>
<Count/>
</CountProvider>
</div>
);
};
export default App;
CountContext.jsx
파일명은 CountContext로 잡았지만 CountProvider로 바꿔서 공급해주는 역할로 만든다.
export const CountContext = createContext();
이제 여기가 공유지이다 선언해주는 것이다.
import React from 'react';
const CountProvider = () => {
return (
<div>
</div>
);
};
export default CountProvider;
Count.jsx
CountContext.Consumer로 바꿔서
여기서는 위에서 보낸 데이터 받는 역할로 만든다.
import React from 'react';
const Count = () => {
return (
<CountContext.Consumer>
{
() => (
<div>
<h1>카운트 : { count }</h1>
<p>
<button onClick={ () => increment() }>증가</button>
<button onClick={ () => decrement() }>감소</button>
</p>
</div>
)
}
</CountContext.Consumer>
);
};
export default Count;
아직은 에러가 뜬다 !!
CountContext.jsx
state, 함수 등 모든 처리를 한다.
Provider에는 value라는 props가 있으며, 이것이 데이터를 하위 컴포넌트에게 전달한다.
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1)
}
const decrement = () => {
setCount(count - 1)
}
자식이 필요한 count increment decrement를 부모인 CountContext가 제공해주겠다 ! 이다.
이제 자식에게 보내주면 된다.
const CountProvider = (props) => {
return (
<CountContext.Provider value={{ count, increment, decrement }}>
{/* children은 부모 컴포넌트에서 자식 엘레먼트나 컴포넌트를 포함할 때 자동으로 전달된다. */}
{ props. children }
</CountContext.Provider>
);
Count.jsx
여기서 이제 보낸 값 받아준다.
import React from 'react';
const Count = () => {
return (
<CountContext.Consumer>
{
({ count, increment, decrement }) => (
<div>
<h1>카운트 : { count }</h1>
<p>
<button onClick={ () => increment() }>증가</button>
<button onClick={ () => decrement() }>감소</button>
</p>
</div>
)
}
</CountContext.Consumer>
);
};
export default Count;
Count.jsx
<CountContext.Consumer>를 쓰니까 코드가 복잡해져서, useContext로 바꾼다.
/*
return (
<CountContext.Consumer>
{
({ count, increment, decrement }) => (
<div>
<h1>카운트 : { count }</h1>
<p>
<button onClick={ () => increment() }>증가</button>
<button onClick={ () => decrement() }>감소</button>
</p>
</div>
)
}
</CountContext.Consumer>
);
*/
주석처리하고
밑에 코드로 바꾼다.
const { count, increment, decrement } = useContext(CountContext);
return (
<div>
<h1>카운트 : {count}</h1>
<p>
<button onClick={() => increment()}>증가</button>
<button onClick={() => decrement()}>감소</button>
</p>
</div>
);
day09
src
components
Count.jsx
Color.jsx
contexts
CountContext.jsx
ColorContext.jsx
App.js
<ColorProvider>
<CountProvider>
<Color/>
</CountProvider>
</ColorProvider>
Color.jsx
import React from 'react';
import { useContext } from 'react';
import { ColorContext } from '../contexts/ColorContext';
import { CountContext } from '../contexts/CountContext';
const Color = () => {
return (
<div>
<h1 style={{ color: color }}>색깔 : { color }, { count }</h1>
<p>
<button onClick={ () => onRed() }>빨강</button>
<button onClick={ () => onGreen() }>초록</button>
<button onClick={ () => onBlue() }>파랑</button>
<button onClick={ () => onMagenta() }>보라</button>
</p>
</div>
);
};
export default Color;
ColorContext.jsx
export const ColorContext = createContext();
맨처음 생성을 해준다 -- 관리자처럼 역할을 해준다는 의미
const ColorProvider = (props) => {
const [color, setColor] = useState('pink');
const onRed = () => {
setColor('red');
}
const onGreen = () => {
setColor('green');
}
const onBlue = () => {
setColor('blue');
}
const onMagenta = () => {
setColor('magenta');
}
상태변수와 거기에 관련된 함수들을 잡아준다.
return (
<ColorContext.Provider value={{ color, onRed, onGreen, onBlue, onMagenta }}>
{ props. children }
</ColorContext.Provider>
);
};
export default ColorProvider;
Color.jsx
위에서 보낸거 받자 !!
const { color, onRed, onGreen, onBlue, onMagenta } = useContext(ColorContext);
count도 필요하니 받자 !
const { count } = useContext(CountContext);
App.js
<ColorProvider>
<CountProvider>
<Color/>
<hr/>
<Count/>
</CountProvider>
</ColorProvider>
ColorContext.jsx
//const ColorProvider = (props) => {
const ColorProvider = ({ children }) => {
{/* { props. children } */}
{ children }
둘 중 편한걸로 쓰면 된다~!
import React from 'react';
import { useState } from 'react';
import { createContext } from 'react';
export const ColorContext = createContext();
//const ColorProvider = (props) => {
const ColorProvider = ({ children }) => {
const [color, setColor] = useState('pink');
const onRed = () => {
setColor('red');
}
const onGreen = () => {
setColor('green');
}
const onBlue = () => {
setColor('blue');
}
const onMagenta = () => {
setColor('magenta');
}
return (
<ColorContext.Provider value={{ color, onRed, onGreen, onBlue, onMagenta }}>
{/* { props. children } */}
{ children }
</ColorContext.Provider>
);
};
export default ColorProvider;
day09
src
components
Count.jsx
Color.jsx
ChangeColor.jsx
ChangeCount.jsx
contexts
CountContext.jsx
ColorContext.jsx
ChangeColorContext.jsx
ChangeCountContext.jsx
App.js
<ChangeColorProvider>
<ChangeCountProvider>
<ChangeColor />
<hr/>
<ChangeCount/>
</ChangeCountProvider>
</ChangeColorProvider>
ChangeColor.jsx
import React from 'react';
const ChangeColor = () => {
return (
<div>
<h1 style={{ color: color }}>글자의 색깔 변경 { color }</h1>
<p>
<button onClick={ () => onColoer('hotpink') }>hotpink</button>
<button onClick={ () => onColoer('skyblue') }>skyblue</button>
<button onClick={ () => onColoer('pink') }>pink</button>
<button onClick={ () => onColoer('tomato') }>tomato</button>
<button onClick={ () => onColoer('lime') }>lime</button>
</p>
</div>
);
};
export default ChangeColor;
ChangeCount.jsx
import React from 'react';
const ChangeCount = () => {
return (
<div>
<h1>카운트 : { count }</h1>
<p>
<button onClick={ () => increment(10) }>10증가</button>
<button onClick={ () => increment(50) }>50증가</button>
<button onClick={ () => decrement(30) }>30감소</button>
<button onClick={ () => decrement(40) }>40감소</button>
</p>
</div>
);
};
export default ChangeCount;
ChangeColorContext.jsx
import React from 'react';
import { useState } from 'react';
import { createContext } from 'react';
import { ColorContext } from './ColorContext';
export const ChangeColorContext = createContext();
const ChangeColorProvider = ({children}) => {
const [color, setColor] = useState('pink');
const onColor = (color) => {
setColor(color);
}
return (
<ChangeColorContext.Provider value={{ color, onColor }}>
{children}
</ChangeColorContext.Provider>
);
};
export default ChangeColorProvider;
ChangeColor.jsx
const { color, onColor } = useContext(ChangeColorContext);
ChangeCountContext.jsx
import React from 'react';
import { createContext } from 'react';
import { useContext } from 'react';
import { useState } from 'react';
export const ChangeCountContext = createContext();
const ChangeCountProvider = ({children}) => {
const [count, setCount] = useState(0);
const increment = (value) => {
setCount( count + value);
}
const decrement = (value) => {
setCount( count - value);
}
return (
<ChangeCountContext.Provider value={{count, increment, decrement}}>
{children}
</ChangeCountContext.Provider>
);
};
export default ChangeCountProvider;
ChangeCount.jsx
const { count, increment, decrement } = useContext(ChangeCountContext);
day09
src
components
Count.jsx
Color.jsx
ChangeColor.jsx
ChangeCount.jsx
Counter.jsx
contexts
CountContext.jsx
ColorContext.jsx
ChangeColorContext.jsx
ChangeCountContext.jsx
CounterContext.jsx
ToggleContext.jsx
App.js
<CounterProvider>
<ToggleProvider>
<Counter/>
</ToggleProvider>
</CounterProvider>
import React from 'react';
const Counter = () => {
return (
<div>
<h1>카운터 : { state.counter }</h1>
<h1 style={{ color: state2.isChk ? 'red' : '#000' }}>토글 : { `${state2.isChk}` }</h1>
<p>
<button onClick={() => dispatch({ type: 'INCREMENT'}) }>증가</button>
<button onClick={() => dispatch({ type: 'DECREMENT'}) }>감소</button>
<button onClick={() => dispatch({ type: 'RESET'}) }>초기화</button>
</p>
<p>
<button onClick={() => dispatch2({ type: 'TOGGLE'}) }>Toggle</button>
<button onClick={() => dispatch2({ type: 'TRUE'}) }>True</button>
<button onClick={() => dispatch2({ type: 'FALSE'}) }>False</button>
</p>
</div>
);
};
export default Counter;
문제풀기
Counter.jsx
import { CounterContext } from '../contexts/CounterContext';
import { ToggleContext } from '../contexts/ToggleContext';
import { useContext } from 'react';
const { state:state, dispatch:dispatch } = useContext(CounterContext);
const { state:state2, dispatch:dispatch2 } = useContext(ToggleContext);
CounterContext.jsx
import React, { createContext, useState } from 'react';
import { useReducer } from 'react';
// 초기값
const initialState = {
counter: 0
};
// 리듀서 만들기
const reducer = (state, action) => {
switch (action.type) {
case 'INCREMENT':
return { counter: state.counter + 1 };
case 'DECREMENT':
return { counter: state.counter - 1 };
case 'RESET':
return { counter: 0 };
default:
return state;
}
};
export const CounterContext = createContext();
const CounterProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<CounterContext.Provider value={{ state, dispatch }}>
{children}
</CounterContext.Provider>
);
};
export default CounterProvider;
ToggleContext.jsx
import React, { createContext, useReducer } from 'react';
// 초기값
const initialState = {
isChk: false,
};
// 리듀서 만들기
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'TOGGLE':
return { isChk: !state.isChk };
case 'TRUE':
return { isChk: true };
case 'FALSE':
return { isChk: false };
default:
return state;
}
};
export const ToggleContext = createContext();
const ToggleProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<ToggleContext.Provider value={{ state, dispatch }}>
{children}
</ToggleContext.Provider>
);
};
export default ToggleProvider;
사용자가 만든 Hooks
CounterContext.jsx
//export const CounterContext = createContext();
//사용자가 만든 Hooks
const CounterContext = createContext();
export const useCounter = () => useContext(CounterContext);
ToggleContext.jsx
//export const ToggleContext = createContext();
//사용자가 만든 Hooks
const ToggleContext = createContext();
export const useToggle = () => useContext(ToggleContext);
Counter.jsx
//import { CounterContext, useCounter } from '../contexts/CounterContext';
//import { ToggleContext } from '../contexts/ToggleContext';
//const { state:state, dispatch:dispatch } = useContext(CounterContext);
//const { state:state2, dispatch:dispatch2 } = useContext(ToggleContext);
import { useCounter } from '../contexts/CounterContext';
import { useToggle } from '../contexts/ToggleContext';
//사용자가 만든 Hooks 이용
const { state, dispatch } = useCounter();
const { state2, dispatch2 } = useToggle();
HOMEWORK
day09
src
components
Todos.jsx
TodosInput.jsx
TodosList.jsx
TodosItem.jsx
contexts
TodosContext.jsx
useTodos 사용자 Hooks 만들어서 사용하기 !!
App.js
<TodosProvider>
<ColorProvider>
<CountProvider>
<Todos/>
<hr/>
<Color/>
<hr/>
<Count/>
</CountProvider>
</ColorProvider>
</TodosProvider>
Todos.jsx
import React, { useContext } from 'react';
import TodosInput from './TodosInput';
import TodosList from './TodosList';
import { ColorContext } from '../contexts/ColorContext';
import { CountContext } from '../contexts/CountContext';
const Todos = () => {
const { count } = useContext(CountContext);
const { color } = useContext(ColorContext);
return (
<div>
<h1 style={{ color }}>할 일 만들기, { color }, { count } </h1>
<TodosInput />
<TodosList />
</div>
);
};
export default Todos;
TodosContext.jsx
import React, { createContext, useRef, useContext, useState } from 'react';
const TodosContext = createContext();
export const useTodos = () => useContext(TodosContext);
const TodosProvider = ({ children }) => {
const [text, setText] = useState('');
const textRef = useRef();
const seq = useRef(1);
const [list, setList] = useState([]);
const onInput = (e) => {
const { value } = e.target;
setText(value);
};
const onAdd = (e) => {
e.preventDefault();
if (!text)
return;
setList([
...list,
{
id: seq.current++,
text: text,
isChecked: false,
},
]);
setText('');
textRef.current.focus();
};
const onSelect = (id) => {
setList(list.map(item =>
item.id === id ? { ...item, isChecked: !item.isChecked } : item
));
};
const onDelete = (id) => {
setList(list.filter(item => item.id !== id));
};
return (
<TodosContext.Provider value={{ text, seq, list, textRef, onInput, onAdd, onDelete, onSelect }}>
{children}
</TodosContext.Provider>
);
};
export default TodosProvider;
TodosInput.jsx
import React from 'react';
import { useTodos } from '../contexts/TodosContext';
const TodosInput = () => {
const { text, onInput, onAdd, textRef } = useTodos();
return (
<div>
<form onSubmit={onAdd}>
<input type='text' name='text' value={text} onChange={onInput} ref={textRef} />
<button type='submit'>추가</button>
</form>
</div>
);
};
export default TodosInput;
TodosList.jsx
import React from 'react';
import TodosItem from './TodosItem';
import { useTodos } from '../contexts/TodosContext';
const TodosList = () => {
const { list } = useTodos();
return (
<div>
<ul>
{list.map(item => (
<TodosItem key={item.id} item={item} />
))}
</ul>
</div>
);
};
export default TodosList;
TodosItem.jsx
import React from 'react';
import { useTodos } from '../contexts/TodosContext';
const TodosItem = ({ item }) => {
const { onSelect, onDelete } = useTodos();
return (
<div>
<li>
<input type="checkbox" checked={item.isChecked} onChange={() => onSelect(item.id)} />
<span style={{ color: item.isChecked ? 'red' : 'black' }}>{item.text}</span>
<button onClick={() => onDelete(item.id)}>삭제</button>
</li>
</div>
);
};
export default TodosItem;
'REACT' 카테고리의 다른 글
DAY 81 - React - Redux (2024.10.31) (0) | 2024.10.31 |
---|---|
DAY 80 - Spring + React +MyBatis(MySQL) - 글 상세보기 (2024.10.30) (0) | 2024.10.30 |
DAY 79, 80 - Spring + React +MyBatis(MySQL) HOMEWORK - 글쓰기 / 글목록 (2024.10.29) (2024.10.30) (0) | 2024.10.30 |
DAY 79 - Spring + React +MyBatis(MySQL) (2024.10.29) (0) | 2024.10.30 |
DAY 78 - React HOMEWORK (2024.10.28) (0) | 2024.10.28 |