React Recoil
리액트의 하나의 컴포넌트에서 데이터를 생성하거나 업데이트하거나 다른 컴포넌트와 데이터를 공유해서 사용하는 여러 방법이 있습니다.
- state 와 props를 사용해서 컴포넌트 간에 데이터를 전달
- React Context API를 사용해서 컴포넌트 간에 데이터를 전달
- Redux, MobX, Recoil 등의 상태 관리 라이브러리를 사용
Recoil이란
- Recoil은 React 상태 관리를 위한 라이브러리로, 전역 상태를 관리하기 위한 간단한 방법입니다.
- Recoil은 atom과 selector를 사용하여 상태를 관리합니다.
Recoil 문법
atom
을 사용하여 상태를 생성합니다.useRecoilState
를 사용하여 상태를 사용합니다.selector
를 사용하여 파생된 상태를 생성합니다.
Recoil 사용하기
atom
을 사용하여 상태를 생성합니다.
import { atom } from 'recoil';
const myState = atom({
key: 'myState', // unique ID (with respect to other atoms/selectors)
default: defaultValue, // default value (aka initial value)
});
useRecoilState
를 사용하여 상태를 사용합니다.
import { useRecoilState } from 'recoil';
const [state, setState] = useRecoilState(myState);
Recoil을 사용한 Theme 예제
import React from 'react';
import { atom, useRecoilState } from 'recoil';
const themes = {
light: {
foreground: '#000000',
background: '#eeeeee',
},
dark: {
foreground: '#ffffff',
background: '#222222',
},
};
const themeState = atom({
key: 'themeState',
default: themes.light,
});
function App() {
return (
<ThemeProvider>
<Toolbar />
</ThemeProvider>
);
}
function ThemeProvider({ children }) {
const [theme, setTheme] = useRecoilState(themeState);
return <div style={{ background: theme.background, color: theme.foreground }}>{children}</div>;
}
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton() {
const [theme] = useRecoilState(themeState);
return <button style={{ background: theme.background, color: theme.foreground }}>I am styled by theme context!</button>;
}
export default App;
Recoil을 이용한 앱 만들기
- 주문 페이지
여러 컴포넌트의 가격과 옵션을 공유하여 총액을 계산하여 보여주는 페이지를 만들어보겠습니다.
import React from 'react';
import { atom, useRecoilState } from 'recoil';
const orderState = atom({
key: 'orderState',
default: {
price: 0,
option: 'none',
},
});
function App() {
return (
<OrderProvider>
<Order />
<Total />
</OrderProvider>
);
}
function OrderProvider({ children }) {
return <div>{children}</div>;
}
function Order() {
const [order, setOrder] = useRecoilState(orderState);
const handleChange = e => {
let price = 0;
switch (e.target.value) {
case 'sugar':
price = 1000;
break;
case 'milk':
price = 1500;
break;
case 'none':
default:
price = 0;
break;
}
setOrder({ ...order, option: e.target.value, price: price });
};
return (
<div>
<h1>Order</h1>
<select
value={order.option}
onChange={handleChange}
>
<option value="none">None</option>
<option value="sugar">Sugar</option>
<option value="milk">Milk</option>
</select>
</div>
);
}
function Total() {
const [order] = useRecoilState(orderState);
return (
<div>
<h1>Total</h1>
<p>Price: {order.price}원</p>
<p>Option: {order.option}</p>
</div>
);
}
export default App;
- 할 일 관리 앱 Recoil 사용하기
// src/context/TodoContext.js
import React from 'react';
import { atom, useRecoilState } from 'recoil';
const initialState = [
{
id: 1,
isDone: false,
task: '고양이 밥주기',
createdDate: new Date().getTime(),
},
{
id: 2,
isDone: false,
task: '감자 캐기',
createdDate: new Date().getTime(),
},
{
id: 3,
isDone: false,
task: '고양이 놀아주기',
createdDate: new Date().getTime(),
},
];
const todoState = atom({
key: 'todoState',
default: initialState,
});
export function useTodoState() {
return useRecoilState(todoState);
}
// src/components/App.js
import React from 'react';
import TodoHd from './TodoHd';
import TodoEditor from './TodoEditor';
import TodoList from './TodoList';
import { RecoilRoot } from 'recoil';
function App() {
return (
<RecoilRoot>
<div>
<TodoHd />
<TodoEditor />
<TodoList />
</div>
</RecoilRoot>
);
}
export default App;
// src/components/TodoList.js
import React, { useState } from 'react';
import TodoItem from './TodoItem';
import { useTodoState } from '../context/TodoContext';
export default function TodoList() {
const [todo, setTodo] = useTodoState();
const [search, setSearch] = useState('');
const onChangeSearch = e => {
setSearch(e.target.value);
};
const filteredTodo = () => {
return todo.filter(item => item.task.toLowerCase().includes(search.toLowerCase()));
};
return (
<div>
<h2>할 일 목록 📃</h2>
<input
value={search}
onChange={onChangeSearch}
placeholder="검색어를 입력하세요"
/>
<ul>
{filteredTodo().map(item => (
<TodoItem
key={item.id}
{...item}
/>
))}
</ul>
</div>
);
}
// src/components/TodoEditor.js
import React, { useState } from 'react';
import { useTodoState } from '../context/TodoContext';
export default function TodoEditor() {
const [todo, setTodo] = useTodoState();
const [task, setTask] = useState('');
const onChangeTask = e => {
setTask(e.target.value);
};
const onSubmit = () => {
if (!task) {
return;
}
setTodo([{ id: Date.now(), isDone: false, task, createdDate: new Date().getTime() }, ...todo]);
setTask('');
};
const onKeyDown = e => {
if (e.key === 'Enter') {
onSubmit();
}
};
return (
<div className="TodoEditor">
<h2>새로운 Todo 작성하기 ✏ </h2>
<div>
<input
value={task}
onChange={onChangeTask}
onKeyDown={onKeyDown}
placeholder="할 일을 추가로 입력해주세요."
/>
<button onClick={onSubmit}>추가</button>
</div>
</div>
);
}
// src/components/TodoItem.js
import React from 'react';
import { useTodoState } from '../context/TodoContext';
export default function TodoItem({ id, isDone, task, createdDate }) {
const [todo, setTodo] = useTodoState();
const onUpdate = () => {
setTodo(todo.map(it => (it.id === id ? { ...it, isDone: !it.isDone } : it)));
};
const onDelete = () => {
setTodo(todo.filter(it => it.id !== id));
};
return (
<div>
<li key={id}>
<input
type="checkbox"
checked={isDone}
onChange={onUpdate}
/>
<span style={{ textDecoration: isDone ? 'line-through' : 'none' }}>{task}</span>
<span>{new Date(createdDate).toLocaleDateString()}</span>
<button onClick={onDelete}>삭제</button>
</li>
</div>
);
}
'Front > React' 카테고리의 다른 글
React Native 심화 (1) | 2024.11.02 |
---|---|
React Native 시작하기 (0) | 2024.11.02 |
조건부 렌더링 (Conditional Rendering) - React 배우기 (0) | 2024.11.02 |
React의 핵심 개념 (1) | 2024.10.27 |
Rudux로 전역 상태 관리하기 (0) | 2024.05.04 |