๐ถ๐ซ๏ธ
45์ผ์ฐจ
Part 13. mobX๋ก ์ํ๊ด๋ฆฌํ๊ธฐ
Part 13. mobX๋ก ์ํ๊ด๋ฆฌํ๊ธฐ
https://slides.com/woongjae/mobx2021
https://mobx.js.org/README.html
- ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ์ ๊ทน ํ์ฉํ๋ค.
- cra ์ ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ์ฌ์ฉํ๋ ๋ฒ...
- ์คํ ์ด ๊ฐ์ฒด์ ๋ถ์ด๋ ๋ฐ์ฝ๋ ์ดํฐ๊ฐ ์๊ณ , => @observable
- ์ปดํฌ๋ํธ์์ ์ฌ์ฉํ๋ ๋ฐ์ฝ๋ ์ดํฐ๊ฐ ์๋ค. => @observer
- TypeScript ๊ฐ Base ์ธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค
- Redux ์ ๋ง์ฐฌ๊ฐ์ง๋ก, ์คํ ์ด์ ํ์ํ ๋ถ๋ถ๊ณผ ๋ฆฌ์กํธ์ ํ์ํ ๋ถ๋ถ์ด ์๋ค.
- npm i mobx -D
- npm i mobx-react -D
- ๋ฆฌ๋์ค์ ๋ค๋ฅด๊ฒ ๋จ์ผ ์คํ ์ด๋ฅผ ๊ฐ์ ํ์ง ์๋๋ค.โ
๐ ํ๋ก์ ํธ์ decorator ์ค์ ํ๊ธฐ
$ npm i customize-cra react-app-rewired -D
$ npm i autobind-decorator
// ./jsconfig.json
{
"compilerOptions": {
"experimentalDecorators": true
}
}
// ./package.json
{
...
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-scripts eject"
},
...
}
// ./config-overrides.js
const { override, addDecoratorsLegacy } = require("customize-cra");
module.exports = override(addDecoratorsLegacy());
// ./src/components/Button.jsx
import React from "react";
import autobind from "autobind-decorator";
export default class Button extends React.Component {
render() {
return <button onClick={this.click}>๋ฒํผ</button>;
}
@autobind
click() {
console.log("clicked");
}
}
// ./src/App.js
import logo from "./logo.svg";
import "./App.css";
import Button from "./components/Button";
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<Button />
</header>
</div>
);
}
export default App;
- mobX with TypeScript
npm i customize-cra react-app-rewired -D # XXX
$ npm i autobind-decorator
// ./tsconfig.json
{
"compilerOptions": {
...
"experimentalDecorators": true
},
...
}
// ./scr/components/Button.jsx
import autobind from "autobind-decorator";
import React from "react";
export default class Button extends React.Component {
render() {
return <button onClick={this.click}>ํด๋ฆญ</button>;
}
@autobind
click() {
console.log("clicked");
}
}
// ./src/App.jsx
import React from "react";
import logo from "./logo.svg";
import "./App.css";
import Button from "./components/Button";
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<Button />
</header>
</div>
);
}
export default App;
๐ @observable (by mobx)
# npm i mobx
- observable(<value>)
- ๋ฐ์ฝ๋ ์ดํฐ ์์ด ์ฌ์ฉํ๋ ๋ฐฉ์
- @ ์์ด, ํจ์์ฒ๋ผ ์ฌ์ฉํด์ ๋ฆฌํดํ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉ
- @observable <ํด๋์ค์ ํ๋กํผํฐ>
- ๋ฐ์ฝ๋ ์ดํฐ๋ก ์ฌ์ฉํ๋ ๋ฒ
- ํด๋์ค ๋ด๋ถ์ ํ๋กํผํฐ ์์ ๋ถ์ฌ์ ์ฌ์ฉ
- ํ ํด๋์ค ์์ ์ฌ๋ฌ๊ฐ์ @observable ์กด์ฌ
// ./src/index.js
...
import { autorun, makeObservable, observable } from "mobx";
const isLogin = observable(true);
const person = observable({ name: "Mark", age: 39 });
class PersonStore {
@observable
name = "Mark";
@observable
age = 39;
constructor() {
makeObservable(this);
}
}
const personStore = new PersonStore();
autorun(() => {
console.log(isLogin.get());
console.log(person.age);
console.log(personStore.age);
});
isLogin.set(false);
person.age = 40;
personStore.age = 40;
...
๐ @observer (by mobx-react)
# npm i mobx-react
- observer(<์ปดํฌ๋ํธ>);
- ๋ฐ์ฝ๋ ์ดํฐ ์์ด ์ฌ์ฉํ๋ ๋ฐฉ์
- ํจ์ ์ปดํฌ๋ํธ์ ์ฌ์ฉ
- <์ปดํฌ๋ํธ ํด๋์ค> ์ @observer ๋ฌ์์ ์ฒ๋ฆฌ
- โํด๋์ค ์ปดํฌ๋ํธ์ ์ฌ์ฉ
// ./contexts/PersonContext.js
import { createContext } from "react";
const PersonContext = createContext();
export default PersonContext;
// ./stores/PersonStore.js
import { makeObservable, observable } from "mobx";
export default class PersonStore {
@observable
name = "Mark";
@observable
age = 39;
constructor() {
makeObservable(this);
}
plus() {
this.age++;
}
}
// ./index.js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import PersonContext from "./contexts/PersonContext";
import PersonStore from "./stores/PersonStore";
const personStore = new PersonStore();
ReactDOM.render(
<React.StrictMode>
<PersonContext.Provider value={personStore}>
<App />
</PersonContext.Provider>
</React.StrictMode>,
document.getElementById("root")
);
...
- Functional Component
// ./App.js
import logo from "./logo.svg";
import "./App.css";
import { observer } from "mobx-react";
import React, { useContext } from "react";
import PersonContext from "./contexts/PersonContext";
function App() {
const personStore = useContext(PersonContext);
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>{personStore.age}</p>
<p>
<button onClick={click}>plus</button>
</p>
</header>
</div>
);
function click() {
personStore.plus();
}
}
export default observer(App);
- Class Component
// ./App.js
// Class Component
import logo from "./logo.svg";
import "./App.css";
import { observer } from "mobx-react";
import React, { useContext } from "react";
import PersonContext from "./contexts/PersonContext";
import autobind from "autobind-decorator";
@observer
class App extends React.Component {
static contextType = PersonContext;
render() {
const personStore = this.context;
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>{personStore.age}</p>
<p>
<button onClick={this.click}>plus</button>
</p>
</header>
</div>
);
}
@autobind
click() {
const personStore = this.context;
personStore.plus();
}
}
export default App;
๐ @computed (by mobx)
- computed(๋ด๋ถ์์ observable ์ ์ฌ์ฉํ๋ ํจ์);
- ๋ฐ์ฝ๋ ์ดํฐ ์์ด ์ฌ์ฉํ๋ ๋ฐฉ์
- <observable ํด๋์ค> ์ getter ์ @computed ๋ฌ์์ ์ฒ๋ฆฌ
- โ์คํ ์ด์ ์ฌ์ฉ
- getter ์๋ง ๋ถ์ผ ์ ์๋ค.
- ํจ์๊ฐ ์๋๋ผ ๋ฆฌ์กํฐ๋ธ ํ๋ค๋ ๊ฒ์ ์ฃผ๋ชฉ
- ์ค์ ์ปดํฌ๋ํธ์์ ์ฌ์ฉํ๋ (๊ฒํฐ)๊ฐ๋ค์ ๋ฌ์์ ์ฌ์ฉํ๋ฉด ์ต์ ๋ฒ์๋ก ๋ณ๊ฒฝํ ์ ์๊ธฐ ๋๋ฌธ์ ์ ์ฉํ๋ค.
- 40์ด์ด ๋์์๋๋ง ๋์ด๋ฅผ ์ฌ๋ฆฌ๋ฉด 40์ด ์ดํ์ผ๋๋ ์ฌ๋๋๋ง ๋์์ด ์๋ ๊ฒ๊ณผ ๊ฐ์ ๊ฒฝ์ฐ
- ๋ด๋ถ์ ์ผ๋ก ๊ณ ๋์ ์ต์ ํ => ์ด๋ป๊ฒ ?
- ๋งค๋ฒ ์ฌ๊ณ์ฐ์ ํ์ง ์๋๋ค
- ๊ณ์ฐ์ ์ฌ์ฉํ observable ๊ฐ์ด ๋ณ๊ฒฝ๋์ง ์์ผ๋ฉด ์ฌ์คํํ์ง ์์.
- ๋ค๋ฅธ computed ๋๋ reaction ์ ์ํด ํธ์ถ๋์ง ์์ผ๋ฉด ์ฌ์คํํ์ง ์์.
- observable ์ด ๋ณํ๋๋ฐ computed ๊ฐ ๋ณํ์ง ์์๋ ๋๋ํ์ง ์์.
- Funtional Component
// ./App.js
import logo from "./logo.svg";
import "./App.css";
import { observer } from "mobx-react";
import React, { useContext } from "react";
import PersonContext from "./contexts/PersonContext";
import autobind from "autobind-decorator";
import { computed } from "mobx";
function App() {
const personStore = useContext(PersonContext);
const age10 = computed(() => {
return Math.floor(personStore.age / 10) * 10;
}).get();
console.log("render");
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>{age10}</p>
<p>
<button onClick={click}>plus</button>
</p>
</header>
</div>
);
function click() {
personStore.plus();
}
}
export default observer(App);
- Class Component
// ./App.js
import logo from "./logo.svg";
import "./App.css";
import { observer } from "mobx-react";
import React, { useContext } from "react";
import PersonContext from "./contexts/PersonContext";
import autobind from "autobind-decorator";
@observer
class App extends React.Component {
static contextType = PersonContext;
render() {
console.log("render");
const personStore = this.context;
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>{personStore.age10}</p>
<p>
<button onClick={this.click}>plus</button>
</p>
</header>
</div>
);
}
@autobind
click() {
const personStore = this.context;
personStore.plus();
}
}
export default App;
// ./stores/PersonStore.js
import { 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);
}
plus() {
this.age++;
}
}
๐ @action (by mobx)
// ./App.js
import logo from "./logo.svg";
import "./App.css";
import { observer } from "mobx-react";
import React, { useContext } from "react";
import PersonContext from "./contexts/PersonContext";
import autobind from "autobind-decorator";
import { action, computed, runInAction } from "mobx";
function App() {
const personStore = useContext(PersonContext);
console.log("render", personStore.age, personStore.name);
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
{personStore.age},{personStore.name}
</p>
<p>
<button onClick={click}>plus</button>
</p>
</header>
</div>
);
function click() {
// personStore.plus();
setTimeout(() => {
personStore.testAction();
}, 500);
}
}
export default observer(App);
// ./stores/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);
}
plus() {
this.age++;
}
@action
testAction() {
this.age = 45;
this.name = "Woongjae";
}
}
๐ @inject ์ Provider => store๊ฐ ์ฌ๋ฌ๊ฐ์ด๊ธฐ ๋๋ฌธ์ ๊ฐ๋ฅํ๋ค!
- ๋ค, ๊ทธ ํ๋ก๋ฐ์ด๋๊ฐ ๋ง์ต๋๋ค.
- ๋ค, ๊ทธ๋์ ์ปจํ ์ด๋๋ผ๋ ๊ฐ๋ ์ ์ฌ์ฉํด๋ ์ข์ต๋๋ค.
- ํ๋ก๋ฐ์ด๋์ props ๋ก ๋ฃ๊ณ , @inject ๋ก ๊บผ๋ด ์ด๋ค๊ณ ์๊ฐํ์๋ฉด ๋ฉ๋๋ค.
- ์๋นํ ๋ช ์์ ์ด๊ณ , ํธํฉ๋๋ค.
- ์ปจํ
์ด๋๋ฅผ ์ฐ์ง ์์๋ ๋ ๊ฒ ๊ฐ์ต๋๋ค.
- props ๋ก ๋ฐ๊ฟ์ค๋๋ค.
- this.props.store
// ./index.js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { observable } from "mobx";
import PersonContext from "./contexts/PersonContext";
import PersonStore from "./stores/PersonStore";
import { Provider } from "mobx-react";
const personStore = new PersonStore();
ReactDOM.render(
<React.StrictMode>
<Provider personStore={personStore}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById("root")
);
...
- Functional Component
// ./App.js
import logo from "./logo.svg";
import "./App.css";
import { inject, observer } from "mobx-react";
import React, { useContext } from "react";
import PersonContext from "./contexts/PersonContext";
import autobind from "autobind-decorator";
import { action, computed, runInAction } from "mobx";
function App({ personStore }) {
// const personStore = useContext(PersonContext);
console.log("render", personStore.age, personStore.name);
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
{personStore.age},{personStore.name}
</p>
<p>
<button onClick={click}>plus</button>
</p>
</header>
</div>
);
function click() {
setTimeout(() => {
personStore.testAction();
}, 500);
}
}
export default inject("personStore")(observer(App));
- Class Component
// ./App.js
import logo from "./logo.svg";
import "./App.css";
import { inject, observer } from "mobx-react";
import React, { useContext } from "react";
import PersonContext from "./contexts/PersonContext";
import autobind from "autobind-decorator";
import { computed } from "mobx";
@inject("personStore")
@observer
class App extends React.Component {
// static contextType = PersonContext;
render() {
console.log("render");
const { personStore } = this.props;
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>{personStore.age10}</p>
<p>
<button onClick={this.click}>plus</button>
</p>
</header>
</div>
);
}
@autobind
click() {
// const personStore = this.context;
this.props.personStore.plus();
}
}
export default App;
- AppContainer๋ฅผ ๋ง๋ค๊ณ AppComponent์๊ฒ ์ ๋ฌํ๋ ๋ฐฉ์์ผ๋ก ๋ณ๊ฒฝ
// ./App.js
import logo from "./logo.svg";
import "./App.css";
import { inject, observer } from "mobx-react";
import React, { useContext } from "react";
import PersonContext from "./contexts/PersonContext";
import autobind from "autobind-decorator";
import { computed } from "mobx";
@inject("personStore")
@observer
class AppContainer extends React.Component {
render() {
console.log("render");
const { personStore } = this.props;
return <App age10={personStore.age10} plus={this.plus} />;
}
@autobind
click() {
this.props.personStore.plus();
}
}
function App({ age10, plus }) {
console.log("render");
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>{age10}</p>
<p>
<button onClick={click}>plus</button>
</p>
</header>
</div>
);
function click() {
plus();
}
}
export default AppContainer;
๐ mobx-devtools
https://github.com/mobxjs/mobx-devtools
๐ stores (์คํ ์ด ์ฌ๋ฌ๊ฐ ์ฌ์ฉํ๊ธฐ)
// ./stores/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(rootStore) {
makeObservable(this);
this.rootStore = rootStore;
}
@action
plus() {
this.age++;
this.rootStore.todoStore.todos = [];
}
@action
testAction() {
this.age = 45;
this.name = "Woongjae";
}
}
// ./stores/TodoStore.js
import { makeObservable, observable } from "mobx";
export default class TodoStore {
@observable
todos = [];
@action
add() {
this.todos.push({ text, done: false });
}
constructor() {
makeObservable(this);
}
}
// ./stores/RootStore.js
import PersonStore from "./PersonStore";
import TodoStore from "./TodoStore";
export default class RootStore {
constructor() {
this.todoStore = new TodoStore(this);
this.personStore = new PersonStore(this);
}
}
// ./index.js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { Provider } from "mobx-react";
import RootStore from "./stores/RootStore";
const rootStore = new RootStore();
ReactDOM.render(
<React.StrictMode>
<Provider {...rootStore}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById("root")
);
...
// ./containers/PersonContainer.jsx
import { inject } from "mobx-react";
import { observer } from "mobx-react-lite";
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));
// ./containers/TodoContainer.jsx
import { inject } from "mobx-react";
import { observer } from "mobx-react-lite";
import Todo from "../components/Todo";
const TodoContainer = ({ todoStore }) => {
const todos = todoStore.todos;
return <Todo todos={todos} />;
};
export default inject("todoStore")(observer(TodoContainer));
// ./containers/TodoFormContainer.jsx
import { inject } from "mobx-react";
import { observer } from "mobx-react-lite";
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));
// ./components/Person.jsx
export default function Person({ age10, plus }) {
return (
<div>
<h1>{age10}</h1>
<p>
<button onClick={click}>plus</button>
</p>
</div>
);
function click() {
plus();
}
}
// ./components/Todo.jsx
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>
);
});
// ./components/TodoForm.jsx
import { useRef } from "react";
export default function TodoForm({ add }) {
const ref = useRef();
return (
<div>
<p>
<input ref={ref} />
<button onClick={click}>add</button>
</p>
</div>
);
function click() {
add(ref.current.value);
}
}
// ./App.js
...
function App({ age10, plus }) {
console.log("render");
return (
<div className="App">
<header className="App-header">
<PersonContainer />
<TodoContainer />
<TodoFormContainer />
</header>
</div>
);
}
export default App;
๐ Asynchronous actions
$ npm i axios
// ./App.js
...
function App({ age10, plus }) {
console.log("render");
return (
<div className="App">
<header className="App-header">
<PersonContainer />
<TodoContainer />
<TodoFormContainer />
<UserListContainer />
</header>
</div>
);
}
export default App;
// ./stores/RootStore.js
import PersonStore from "./PersonStore";
import TodoStore from "./TodoStore";
import UserStore from "./UserStore";
export default class RootStore {
constructor() {
this.todoStore = new TodoStore(this);
this.personStore = new PersonStore(this);
this.userStore = new UserStore(this);
}
}
// ./components/UserList.jsx
import { useEffect } from "react";
export default function UserList({ getUsers, users }) {
useEffect(() => {
getUsers();
}, [getUsers]);
return (
<div>
<u1>
{users.map((user) => {
<l1>{user.login}</l1>;
})}
</u1>
</div>
);
}
- ์ปจํ ์ด๋์์ ๋น๋๊ธฐ ๋ก์ง์ ๊ตฌํํ๊ณ , ๋จ๊ณ๋ณ๋ก state ๋ฅผ ๋ณ๊ฒฝํ๋ action ์ ๋ณ๋๋ก ๋ถ๋ฆฌํด์ ํธ์ถํ๋ ๋ฐฉ์
// ./stores/UserStore.js
import { makeObservable, observable } from "mobx";
export default class UserStore {
@observable
state = {
users: [],
loading: false,
error: null,
};
constructor() {
makeObservable(this);
}
@action
pending() {
this.state.loading = true;
this.state.error = null;
}
@action
success(users) {
this.state.users = users;
this.state.loading = false;
this.state.error = null;
}
@action
fail(error) {
this.state.loading = false;
this.state.error = error;
}
}
// ./containers/UserListContainer.jsx
import { inject } from "mobx-react";
import { observer } from "mobx-react-lite";
import { useCallback } from "react";
import UserList from "../components/UserList";
import axios from "axios";
const UserListContainer = ({ userStore }) => {
const getUsers = useCallback(async () => {
try {
userStore.pending();
const response = await axios.get("https://api.github.com/users");
userStore.success(response.data);
} catch (error) {
userStore.fail(error);
}
}, [userStore]);
const users = userStore.state.users;
return <UserList getUsers={getUsers} users={users} />;
};
export default inject("userStore")(observer(UserListContainer));
- action ์์์ ๋น๋๊ธฐ ๋ก์ง์ ๊ตฌํํ๊ณ , state ๋ฅผ ๋ณ๊ฒฝํด์ผ ํ ๋ runInAction() ํจ์๋ฅผ ์จ์ ํ์ด๋ฐ๋ง๋ค state ๋ฅผ ๋ณ๊ฒฝํ๋ ๋ฐฉ์ โญ
// ./stores/UserStore.js
import axios from "axios";
import { makeObservable, observable, runInAction } from "mobx";
export default class UserStore {
@observable
state = {
users: [],
loading: false,
error: null,
};
constructor() {
makeObservable(this);
}
@action
pending() {
this.state.loading = true;
this.state.error = null;
}
@action
success(users) {
this.state.users = users;
this.state.loading = false;
this.state.error = null;
}
@action
fail(error) {
this.state.loading = false;
this.state.error = error;
}
async getUser() {
try {
// pending
runInAction(() => {
this.state.loading = true;
this.state.error = null;
});
const response = await axios.get("https://api.github.com/users");
// success
runInAction(() => {
this.state.users = response.data;
this.state.loading = false;
this.state.error = null;
});
} catch (error) {
runInAction(() => {
this.state.loading = false;
this.state.error = error;
});
}
}
}
// ./containers/UserListContainer.jsx
import { inject } from "mobx-react";
import { observer } from "mobx-react-lite";
import { useCallback } from "react";
import UserList from "../components/UserList";
const UserListContainer = ({ userStore }) => {
const getUsers = useCallback(() => {
userStore.getUsers();
}, [userStore]);
const users = userStore.state.users;
return <UserList getUsers={getUsers} users={users} />;
};
export default inject("userStore")(observer(UserListContainer));
- generater ๋ฅผ ์ฌ์ฉํด์ ๋น๋๊ธฐ ๋ก์ง์ ๊ตฌํํ๋ ๋ฐฉ์
// ./stores/UserStore.js
import axios from "axios";
import { makeObservable, observable, runInAction } from "mobx";
export default class UserStore {
@observable
state = {
users: [],
loading: false,
error: null,
};
constructor() {
makeObservable(this);
}
@action
pending() {
this.state.loading = true;
this.state.error = null;
}
@action
success(users) {
this.state.users = users;
this.state.loading = false;
this.state.error = null;
}
@action
fail(error) {
this.state.loading = false;
this.state.error = error;
}
@flow
*getUsersFlow() {
try {
this.state.loading = true;
this.state.error = null;
const response = yield axios.get("https://api.github.com/users");
// success
this.state.users = response.data;
this.state.loading = false;
this.state.error = null;
} catch (error) {
this.state.loading = false;
this.state.error = error;
}
}
}
// ./containers/UserListContainer.jsx
import { inject } from "mobx-react";
import { observer } from "mobx-react-lite";
import { useCallback } from "react";
import UserList from "../components/UserList";
const UserListContainer = ({ userStore }) => {
const getUsers = useCallback(() => {
userStore.getUsersFlow();
}, [userStore]);
const users = userStore.state.users;
return <UserList getUsers={getUsers} users={users} />;
};
export default inject("userStore")(observer(UserListContainer));
'๐ฌ > ใ ใ ใ ใ ใ ใ ์ฑ๋ฆฐ์ง' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
47์ผ์ฐจ (0) | 2022.03.29 |
---|---|
46์ผ์ฐจ (0) | 2022.03.29 |
43, 44์ผ์ฐจ (0) | 2022.03.26 |
42์ผ์ฐจ (0) | 2022.03.26 |
41์ผ์ฐจ (0) | 2022.03.25 |