Skip to content

Commit 1fc608e

Browse files
author
chungheon_yi
committed
docs: tictactoe usecase add
1 parent 5bc18dc commit 1fc608e

File tree

12 files changed

+88
-43
lines changed

12 files changed

+88
-43
lines changed

apps/docs/stories/scope/tictactoe/App.tsx

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
import { ComposeProviders } from '@lodado/react-namespace'
22
import { useCallback } from 'react'
33

4-
import { BoardProvider, createUser1Scope, createUser2Scope, RepositoryProvider } from './components/Provider'
4+
import {
5+
BoardProvider,
6+
createUser1Scope,
7+
createUser2Scope,
8+
RepositoryProvider,
9+
ScopeContainerProvider,
10+
} from './components/Provider'
511
import TicTacToeGame from './components/TicTacToeGame'
612
import PlayerRepository from './models/PlayerRepository'
713
import TicTacToe from './models/TicTacToe'
814

9-
export const App = () => {
15+
export const TicTacToeExample = () => {
1016
const game = useCallback(() => new TicTacToe(), [])
1117
const player1Repo = useCallback(() => new PlayerRepository('🔵'), [])
1218
const player2Repo = useCallback(() => new PlayerRepository('❌'), [])
@@ -18,16 +24,15 @@ export const App = () => {
1824
<ComposeProviders
1925
providers={[
2026
<BoardProvider overwriteStore={game} />,
27+
<ScopeContainerProvider value={{ user1, user2 }} />,
28+
2129
<RepositoryProvider scope={user1.__scopeTicTacToeRepository} overwriteStore={player1Repo} />,
2230
<RepositoryProvider scope={user2.__scopeTicTacToeRepository} overwriteStore={player2Repo} />,
2331
]}
2432
>
2533
<div className="App">
2634
<h1>TicTacToe example</h1>
27-
<TicTacToeGame
28-
player1Scope={user1.__scopeTicTacToeRepository}
29-
player2Scope={user2.__scopeTicTacToeRepository}
30-
/>
35+
<TicTacToeGame />
3136
</div>
3237
</ComposeProviders>
3338
)

apps/docs/stories/scope/tictactoe/TicTacToegame.stories.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* eslint-disable max-classes-per-file */
22

3-
import { App } from './App'
3+
import { TicTacToeExample } from './App'
44

55
const meta = {
66
title: 'scope/TicTacToeExample',
@@ -12,5 +12,5 @@ const meta = {
1212
export default meta
1313

1414
export const Example = () => {
15-
return <App />
15+
return <TicTacToeExample />
1616
}

apps/docs/stories/scope/tictactoe/components/Provider.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createNamespaceContext, createNamespaceScope } from '@lodado/react-namespace'
1+
import { createNamespaceContext, createNamespaceScope, createScopeContainer, Scope } from '@lodado/react-namespace'
22

33
import PlayerRepository from '../models/PlayerRepository'
44
import TicTacToe from '../models/TicTacToe'
@@ -14,6 +14,12 @@ export const {
1414
export const createUser1Scope = createRepositoryScope()
1515
export const createUser2Scope = createRepositoryScope()
1616

17-
export const { Provider: BoardProvider, useNamespaceContext: useBoardContext } = createNamespaceContext({
17+
export const {
18+
Provider: BoardProvider,
19+
useNamespaceStores: useBoardNamespaceStores,
20+
useNamespaceContext: useBoardNamespaceContext,
21+
} = createNamespaceContext({
1822
globalStore: () => new TicTacToe(),
1923
})
24+
25+
export const { ScopeContainerProvider, useScopeContainer } = createScopeContainer<'user1' | 'user2'>()

apps/docs/stories/scope/tictactoe/components/TicTacToeGame.tsx

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,26 @@
1-
import { Scope } from '@lodado/react-namespace'
2-
import { useCallback, useMemo, useState } from 'react'
3-
4-
import TicTacToe from '../models/TicTacToe'
51
import { TicTacToeUseCase } from '../models/TicTacToeUsecase'
6-
import { useBoardContext, useRepositoryContext } from './Provider'
2+
import { useBoardNamespaceContext, useBoardNamespaceStores, useRepositoryContext, useScopeContainer } from './Provider'
3+
4+
const TicTacToeGame = () => {
5+
const { user1: player1Scope, user2: player2Scope } = useScopeContainer()
76

8-
const TicTacToeGame = ({ player1Scope, player2Scope }: { player1Scope: Scope<any>; player2Scope: Scope<any> }) => {
9-
const [turn, setTurn] = useState(0)
7+
const { turn, board } = useBoardNamespaceStores((state) => {
8+
return { turn: state.turn, board: state.board }
9+
})
1010

1111
// you can swap scope whatever you want
1212
const currentUserScope = turn % 2 === 0 ? player1Scope : player2Scope
1313

14-
const game = useBoardContext()!
15-
const repository = useRepositoryContext(currentUserScope)!
14+
const game = useBoardNamespaceContext()!
15+
const repository = useRepositoryContext(currentUserScope.__scopeTicTacToeRepository)!
1616
const useCase = new TicTacToeUseCase(game, repository)
1717

1818
const handleMove = async (row: number, col: number) => {
1919
await useCase.play({ row, col })
20-
setTurn(turn + 1)
2120
}
2221

2322
const resetGame = async () => {
2423
await useCase.resetGame()
25-
setTurn(turn + 1)
2624
}
2725

2826
return (
@@ -32,7 +30,7 @@ const TicTacToeGame = ({ player1Scope, player2Scope }: { player1Scope: Scope<any
3230

3331
{game.getWinner() && <p>Winner: {game.getWinner()}</p>}
3432
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 100px)' }}>
35-
{game.getBoard().map((row, rowIndex) =>
33+
{board.map((row, rowIndex) =>
3634
row.map((cell, colIndex) => (
3735
<button
3836
type="button"

apps/docs/stories/scope/tictactoe/models/TicTacToe.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ import { NamespaceStore } from '@lodado/namespace-core'
66
export default class TicTacToe extends NamespaceStore<{
77
board: (string | null)[][]
88
winner: string | 'Draw' | null
9+
turn: number
910
}> {
1011
constructor() {
1112
super({
1213
board: Array(3)
1314
.fill(null)
1415
.map(() => Array(3).fill(null)),
1516
winner: null,
17+
turn: 0,
1618
})
1719
}
1820

@@ -45,16 +47,18 @@ export default class TicTacToe extends NamespaceStore<{
4547

4648
this.state.board[row][col] = icon
4749
this.checkWinner(icon)
50+
this.state.turn += 1
51+
}
52+
53+
updateTurn(): void {
54+
this.state.turn += 1
4855
}
4956

5057
/**
5158
* Reset the game to its initial state.
5259
*/
5360
reset(): void {
54-
this.state.board = Array(3)
55-
.fill(null)
56-
.map(() => Array(3).fill(null))
57-
this.state.winner = null
61+
this.reset()
5862
}
5963

6064
/**
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
'use client'
2+
3+
import { createContext, FC, ReactNode, useContext, useMemo } from 'react'
4+
5+
import { JSON_KEY_TYPE, Scope } from '../../type'
6+
7+
export type ScopeContainer<KEY_TYPE extends JSON_KEY_TYPE> = {
8+
[key in KEY_TYPE]: { [__scopeProp in JSON_KEY_TYPE]: Scope<any> }
9+
}
10+
11+
export function createScopeContainer<T extends JSON_KEY_TYPE>() {
12+
const ScopeContext = createContext<ScopeContainer<T> | null>(null)
13+
14+
const ScopeContainerProvider: FC<{
15+
value: ScopeContainer<T>
16+
children?: ReactNode
17+
}> = ({ value, children }) => {
18+
return <ScopeContext.Provider value={useMemo(() => value, Object.keys(value))}>{children}</ScopeContext.Provider>
19+
}
20+
21+
const useScopeContainer = () => {
22+
const context = useContext(ScopeContext)
23+
if (context === null) {
24+
throw new Error('useScope must be used within a ScopeProvider')
25+
}
26+
return context
27+
}
28+
29+
return { ScopeContainerProvider, useScopeContainer }
30+
}

apps/react-namespace/src/components/providerHelper/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@ import ComposeProviders from './ComposeProviders'
22
import WithComposeProviders from './WithComposeProvider'
33

44
export * from './ComposeProviders'
5+
export * from './ScopeContainerProvider'
56
export * from './WithComposeProvider'
67
export { ComposeProviders, WithComposeProviders }

apps/react-namespace/src/createNamespaceContext.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ describe('TestComponent with NamespaceContext', () => {
180180
})
181181

182182
describe('TestComponent with only overwriteStore', () => {
183-
const { Provider, useNamespaceStores } = createNamespaceContext<TestStore['state'], TestStore>({})
183+
const { Provider, useNamespaceStores } = createNamespaceContext<TestStore>({})
184184

185185
const TestComponent2 = () => {
186186
const { count, increment, decrement, reset } = useNamespaceStores((state) => ({ count: state.count }))

apps/react-namespace/src/createNamespaceContext.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ import { createContext, FC, ReactNode, useContext, useMemo } from 'react'
77
import { StoreOption } from './type'
88
import { createNamespaceHooks } from './utils/createNamespaceHooks'
99

10-
export function createNamespaceContext<
11-
State extends Record<string | symbol, any>,
12-
StoreType extends NamespaceStore<State>,
13-
>({ globalStore: globalStoreDIP, localStore }: StoreOption<State, StoreType>) {
10+
export function createNamespaceContext<StoreType extends NamespaceStore<Record<string | symbol, any>>>({
11+
globalStore: globalStoreDIP,
12+
localStore,
13+
}: StoreOption<StoreType['state'], StoreType>) {
1414
const Context = createContext<StoreType | undefined>(undefined)
1515
const globalStore = globalStoreDIP && typeof globalStoreDIP === 'function' ? globalStoreDIP() : globalStoreDIP
1616

1717
const { useNamespaceStores, useNamespaceAction, useNamespaceContext } = createNamespaceHooks<
18-
State,
18+
StoreType['state'],
1919
StoreType,
2020
undefined
2121
>(() => useContext(Context))

apps/react-namespace/src/createNamespaceScope.tsx

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,12 @@
11
/* eslint-disable no-shadow */
22
import { NamespaceStore } from '@lodado/namespace-core'
33
import { isNil } from 'lodash-es'
4-
import React, { Context, createContext, FC, ReactNode, useContext, useMemo } from 'react'
4+
import { createContext, FC, ReactNode, useContext, useMemo } from 'react'
55

66
import { createNamespaceContext } from './createNamespaceContext'
7-
import { StoreOption } from './type'
7+
import { CreateScope, Scope, StoreOption } from './type'
88
import { createNamespaceHooks } from './utils/createNamespaceHooks'
99

10-
export type Scope<C = any> = { [scopeName: string]: Context<C>[] } | undefined
11-
export type ScopeHook = (scope: Scope) => { [__scopeProp: string]: Scope }
12-
export interface CreateScope {
13-
scopeName: string
14-
(): ScopeHook
15-
}
16-
1710
export function composeContextScopes(...scopes: CreateScope[]) {
1811
const baseScope = scopes[0]
1912
if (scopes.length === 1) return baseScope

0 commit comments

Comments
 (0)