Skip to content

Commit

Permalink
feat: update react demo
Browse files Browse the repository at this point in the history
  • Loading branch information
Aqours committed Nov 3, 2023
1 parent 62b5903 commit 5263b29
Show file tree
Hide file tree
Showing 16 changed files with 203 additions and 41 deletions.
3 changes: 3 additions & 0 deletions apps/react-demo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# README

It's a demo which help developer to understand the react-mobx project structure.
2 changes: 2 additions & 0 deletions apps/react-demo/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
</head>
<body>
<div id="app1"></div>
<hr>
<div id="app2"></div>
<hr>
<div id="app3"></div>
<script src="app.js"></script>
</body>
Expand Down
18 changes: 11 additions & 7 deletions apps/react-demo/src/app.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import './style/global.less';
import React from 'react';
import { createRoot, Root } from 'react-dom/client';
import { RootStore } from './store/root.store';
import { JetbrainsLogo } from '@apex/svg-project';
import { RootContext } from './context';
import { TimerView } from './view/timer.view';
import { TimerView } from './component/mobx/Timer.View';
import { ThemeSwitchView, ThemeSwitchView2 } from './component/mobx/ThemeSwitch.View';

export class App {
root: Root;
rootStore: RootStore;
timer: number;

constructor(readonly container: HTMLElement, readonly rate?: number, readonly node?: HTMLElement) {
constructor(
readonly container: HTMLElement,
readonly rate?: number,
readonly node?: HTMLElement,
) {
this.rootStore = new RootStore();
this.root = createRoot(container);
this.render();
Expand All @@ -19,19 +25,17 @@ export class App {

render() {
this.root.render(
// @ts-ignore
// @see https://github.com/facebook/react/issues/24304#issuecomment-1094565891
<RootContext.Provider value={this.rootStore}>
<TimerView node={this.node} tip={this.rate === 1 || this.rate == null ? null : `${this.rate}x`} />
<p style={{ width: 128 }} dangerouslySetInnerHTML={{ __html: JetbrainsLogo }} />
<ThemeSwitchView />
<ThemeSwitchView2 />
</RootContext.Provider>,
);
}

startCount() {
this.timer = window.setInterval(() => {
this.rootStore.timerStore.increaseTimer(this.rate);
}, 1000);
this.timer = window.setInterval(() => this.rootStore.timerStore.increaseTimer(this.rate), 1000);
}

dispose() {
Expand Down
21 changes: 21 additions & 0 deletions apps/react-demo/src/component/atom/Counter.Atom.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useEffect, useState } from 'react';

/**
* @desc Pure React Component (Can reuse in other project)
* @ai This code snippet defines a React component called CounterAtom.
* It uses the useState hook to create a local state variable called count and a function called setCount to update it.
* The useEffect hook is used to execute a timer that increments the count variable every second.
* The component renders a paragraph element displaying the current value of count.
*/
export const CounterAtom = () => {
// Local state (React)
const [count, setCount] = useState(0);

// Local counter
useEffect(() => {
const timerId = setInterval(() => setCount((n) => n + 1), 1000);
return () => clearInterval(timerId);
}, []);

return <p>Local counter: {count} tick</p>;
};
11 changes: 11 additions & 0 deletions apps/react-demo/src/component/atom/Heading.Atom.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { memo } from 'react';

/**
* @desc Pure React Component (Can reuse in other project)
* @desc React memo example
*/
export const HeadingAtom = memo(() => {
// Check console message in devtool
console.log('[debug] Every component should be memoized and log once');
return <h2>Hello world! (Static Content)</h2>;
});
43 changes: 43 additions & 0 deletions apps/react-demo/src/component/atom/Timer.Atom.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { useEffect, useRef } from 'react';
import { CounterAtom } from './Counter.Atom';
import { HeadingAtom } from './Heading.Atom';

interface ITimerAtomProps {
timePassed: number;
tip?: string;
node?: HTMLElement;
}

/**
* @desc Pure React Component (Can reuse in other project)
* @ai This code snippet defines a React component called TimerAtom.
* It takes in props node, timePassed, and tip. Inside the component, it creates a reference using the useRef hook.
* It also uses the useEffect hook to append the node to the ref.current element when the component mounts.
* The useEffect hook also returns a cleanup function that removes the node from the ref.current element when the component unmounts.
* The component renders a CounterAtom component and a paragraph element that displays the timePassed prop.
* If the tip prop is provided, it appends the tip value to the paragraph element.
*/
export const TimerAtom = ({ node, timePassed, tip }: ITimerAtomProps) => {
const ref = useRef<HTMLParagraphElement>();

useEffect(() => {
node && ref.current.append(node);
return () => node && node.remove();
}, []);

return (
<>
{/* It's a memo component */}
<HeadingAtom />

{/* It's a pure react component */}
{/* DON'T USE THIS in Mobx, it's only a demo */}
{/* Wrap it with Mobx.observer if you want to use it in mobx environment */}
<CounterAtom />

<p ref={ref}>
Time passed: {timePassed}s{tip ? ` (${tip})` : null}
</p>
</>
);
};
36 changes: 36 additions & 0 deletions apps/react-demo/src/component/example.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Example

## 1. Multi File

file1.tsx

```tsx
export const ExampleAtom = () => <p>example</p>
```

file2.tsx

```tsx
import { observer } from 'mobx-react-lite';
import { ExampleAtom } from './file1';

export const Example = observer(ExampleAtom)
```

## 2. Single File (decoupling)

```tsx
import { observer } from 'mobx-react-lite';

const ExampleAtom = () => <p>example</p>

export const Example = observer(ExampleAtom)
```

## 3. Single File (coupling)

```tsx
import { observer } from 'mobx-react-lite';

export const Example = observer(() => <p>example</p>)
```
25 changes: 25 additions & 0 deletions apps/react-demo/src/component/mobx/ThemeSwitch.View.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { observer } from 'mobx-react-lite';
import { useStore } from '../../context';
import { useEffect } from 'react';
import { themeStore } from '../../store/global/theme.store';

export const ThemeSwitchView = observer(() => {
useEffect(() => {
document.body.dataset.theme = themeStore.theme;
}, [themeStore.theme]);

return <button onClick={() => themeStore.toggle()}>Switch Theme (import global store)</button>;
});

export const ThemeSwitchView2 = observer(() => {
/**
* Use ReactContext instead of import variable.
*/
const { themeStore } = useStore();

useEffect(() => {
document.body.dataset.theme = themeStore.theme;
}, [themeStore.theme]);

return <button onClick={() => themeStore.toggle()}>Switch Theme2 (useContext)</button>;
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { TimerCom } from '../gallery/timer.com';
import { useStore } from '../context';
import { TimerAtom } from '../atom/Timer.Atom';
import { useStore } from '../../context';
import { observer } from 'mobx-react-lite';

interface ITimeViewProps {
Expand All @@ -11,7 +11,8 @@ interface ITimeViewProps {
* @desc View with Store (Cannot reuse without same store)
*/
export const TimerView = observer((props: ITimeViewProps) => {
// App store (Mobx)
const { timerStore } = useStore();

return <TimerCom timePassed={timerStore.timePassed} {...props} />;
return <TimerAtom timePassed={timerStore.timePassed} {...props} />;
});
27 changes: 0 additions & 27 deletions apps/react-demo/src/gallery/timer.com.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { makeAutoObservable } from 'mobx';
import { RootStore } from './root.store';
import { RootStore } from '../root.store';

/**
* @desc App Store
*/
export class TimerStore {
timePassed = 0;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { RootStore } from './root.store';
import { RootStore } from '../root.store';
import { makeAutoObservable } from 'mobx';

/**
* @desc App Store
*/
export class UiStore {
constructor(readonly rootStore: RootStore) {
makeAutoObservable(this);
Expand Down
7 changes: 7 additions & 0 deletions apps/react-demo/src/store/example.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Example

| State Scope | Description |
|------------------|---------------------------------------------------------|
| Local | Component private state |
| App (Instance) | App shared state (Partial shared or App shared) |
| Global (Sington) | Global state for multi app instances and all components |
18 changes: 18 additions & 0 deletions apps/react-demo/src/store/global/theme.store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { makeAutoObservable } from 'mobx';

/**
* @desc Global Store (Singleton)
*/
class ThemeStore {
theme = 'light';

constructor() {
makeAutoObservable(this);
}

toggle() {
this.theme = this.theme === 'light' ? 'dark' : 'light';
}
}

export const themeStore = new ThemeStore();
7 changes: 5 additions & 2 deletions apps/react-demo/src/store/root.store.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { TimerStore } from './timer.store';
import { UiStore } from './ui.store';
import { TimerStore } from './app/timer.store';
import { UiStore } from './app/ui.store';
import { themeStore } from './global/theme.store';

export class RootStore {
themeStore = themeStore;

uiStore = new UiStore(this);
timerStore = new TimerStore(this);
}
9 changes: 9 additions & 0 deletions apps/react-demo/src/style/global.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
body[data-theme="light"] {
background-color: #fff;
color: #000;
}

body[data-theme="dark"] {
background-color: #000;
color: #fff;
}

0 comments on commit 5263b29

Please sign in to comment.