이번에는 하나의 스토어가 아닌 여러개의 스토어를 사용해보자!
TodoStore와 RootStore를 만들어보자
// todoStore
import { observable } from "mobx";
export default class TodoStore {
@observable
todos = [];
}
// rootStore
import PersonStore from "./PersonStore";
import TodoStore from "./TodoStore";
export default class RootStore {
constructor() {
this.todoStore = new TodoStore();
this.personStore = new PersonStore();
}
}
이제 index.js에서는 TodoStore와 PersonStore를 각각 만들게 아니라 RootStore를 생성해서 Provider에 props로 넣어주자
// index.js
const rootStore = new RootStore();
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<Provider {...rootStore}>
<App />
</Provider>
</React.StrictMode>
);
이렇게 넣게 되면 RootStore 안에 있는 .PersonStore와 .TodoStore가 각각 풀려서 들어가게 된다.
컨테이너를 만들어 코드들을 분리 해 보자
PersonContainer.jsx
import { inject, observer } from "mobx-react";
import { useCallback } from "react";
import Person from "../components/Person";
const PersonContainer = ({ personStore }) => {
const age10 = personStore.age10;
const plus = useCallback(() => {
personStore.plus();
}, [personStore]);
return <Person age10={age10} plus={plus} />;
};
export default inject("personStore")(observer(PersonContainer));
Person 컴포넌트
export default function Person({ age10, plus }) {
return (
<div>
<h1>{age10}</h1>
<p>
<button onClick={plus}>plus</button>
</p>
</div>
);
}
기존에 있던 App.js 코드들은 사실상 대부분 필요가 없기 때문에 정리해 보자
import "./App.css";
import React from "react";
import PersonContainer from "./\\bcontainers/PersonContainer";
function App() {
return (
<div className="App">
<header className="App-header">
<PersonContainer />
</header>
</div>
);
}
export default App;
이렇게 간단하게 분리하고 다시 실행해 보면 정상적으로 작동하지만
Since strict-mode is enabled, changing (observed) observable values without using an action is not allowed.
액션을 달지 않은 observable들을 변경하는 메서드는 허락되지 않는다라는 경고가 뜬다. 그러면 personStore.plus() 하는게 지금 액션이 안 달려 있어서 그런거니 액션을 달아주자.
// personStore.js
import { action, computed, makeObservable, observable } from "mobx";
export default class PersonStore {
@observable
name = "Mark";
@observable
age = 39;
@computed
get age10() {
return Math.floor(this.age / 10) * 10;
}
constructor() {
makeObservable(this);
}
@action // <- 이 부분에 action을 달아주자
plus() {
this.age++;
}
@action
testAction() {
this.age = 45;
this.name = "Kyusung";
}
}
이제는 TodoContainer와 컴포넌트를 작성해 보자
// TodoContainer.jsx
import { inject, observer } from "mobx-react";
import Todo from "../components/Todo";
const TodoContainer = ({ todoStore }) => {
const todos = todoStore.todos;
return <Todo todos={todos} />;
};
export default inject("todoStore")(observer(TodoContainer));
// Todo.jsx
export default function Todo({ todos }) {
if (todos.length === 0)
return (
<div>
<h1>할 일이 없습니다</h1>
</div>
);
return (
<div>
<ul>
{todos.map(todo => (
<li>{todo.text}</li>
))}
</ul>
</div>
);
}
// app.js
import "./App.css";
import React from "react";
import PersonContainer from "./\\bcontainers/PersonContainer";
import TodoContainer from "./\\bcontainers/TodoContainer";
function App() {
return (
<div className="App">
<header className="App-header">
<PersonContainer />
<TodoContainer />
</header>
</div>
);
}
export default App;
결과
이제 todo 데이터를 추가해 보자
// TodoFormContainer.jsx
import { inject, observer } from "mobx-react";
import { useCallback } from "react";
import TodoForm from "../components/TodoForm";
const TodoFormContainer = ({ todoStore }) => {
const add = useCallback(
text => {
todoStore.add(text);
},
[todoStore]
);
return <TodoForm add={add} />;
};
export default inject("todoStore")(observer(TodoFormContainer));
// TodoForm.jsx
import { useRef } from "react";
export default function TodoForm({ add }) {
const ref = useRef();
return (
<div>
<p>
<input type="text" ref={ref} />
<button onClick={() => add(ref.current.value)}>Add</button>
</p>
</div>
);
}
// TodoStore
import { action, observable } from "mobx";
export default class TodoStore {
@observable
todos = [];
@action
add(text) {
this.todos.push({ text, done: false });
}
}
// App.js
import "./App.css";
import React from "react";
import PersonContainer from "./\\bcontainers/PersonContainer";
import TodoContainer from "./\\bcontainers/TodoContainer";
import TodoFormContainer from "./\\bcontainers/TodoFormContainer";
function App() {
return (
<div className="App">
<header className="App-header">
<PersonContainer />
<TodoContainer />
<TodoFormContainer />
</header>
</div>
);
}
export default App;
이렇게 추가하고 동작해 보면
add 버튼을 눌러도 아무런 변화가 일어나지 않는다. 왜냐면 TodoStore에 우리가 todos를 observable로 설정해 줬지만 이 안에 있는 것은 constructor()를 통해서 makeObservable 함수를 호출하면서 this를 담아서 호출해 줘야 한다.
import { action, makeObservable, observable } from "mobx";
export default class TodoStore {
@observable
todos = [];
@action
add(text) {
this.todos.push({ text, done: false });
}
constructor() {
makeObservable(this);
}
}
그리고 우리는 지금 TodoContainer에서 observer를 했기 때문에 Todo가 observer를 안 해도 된다고 생각할 수도 있지만 Todo가 mobx에 영향을 받아서 다시 렌더가 될려면 Todo에도 observer가 있어야 한다.
import { observer } from "mobx-react";
export default observer(function Todo({ todos }) {
if (todos.length === 0)
return (
<div>
<h1>할 일이 없습니다</h1>
</div>
);
return (
<div>
<ul>
{todos.map(todo => (
<li>{todo.text}</li>
))}
</ul>
</div>
);
});
정상적으로 추가가 되는 걸 볼 수 있다.
'Front-End > MobX' 카테고리의 다른 글
Asynchronous actions (0) | 2022.12.16 |
---|---|
@inject 와 Provider (0) | 2022.12.16 |
@action (0) | 2022.12.15 |
@Computed (0) | 2022.12.15 |
@Observer (0) | 2022.12.15 |