Skip to content

Commit

Permalink
fix: optimize react scene use context code
Browse files Browse the repository at this point in the history
  • Loading branch information
zhangyuang committed Mar 25, 2024
1 parent 64382dc commit 1d3e885
Show file tree
Hide file tree
Showing 24 changed files with 147 additions and 180 deletions.
15 changes: 1 addition & 14 deletions @types/context/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,11 @@
declare module '_build/create-context' {
import type { Context, Dispatch } from 'react'
interface IContext<T = any> {
state?: T
dispatch?: Dispatch<Action>
}
interface Action {
type: string
payload: object
}
const STORE_CONTEXT: Context<IContext>
}

declare module 'ssr-deepclone' {
const deepClone: (obj: any) => any
}

declare module '_build/ssr-declare-routes' { }
declare module '_build/ssr-manual-routes' { }
declare module '_build/staticConfig' {}
declare module '_build/staticConfig' { }
declare module 'koa2-connect' {
export default (params: any): any => { }
}

5 changes: 1 addition & 4 deletions packages/cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const spinner = {
}

const startOrBuild = async (argv: Argv, type: 'start' | 'build') => {
const { copyReactContext, judgeFramework, judgeServerFramework, logGreen } = await import('ssr-common-utils')
const { judgeFramework, judgeServerFramework, logGreen } = await import('ssr-common-utils')
const framework = judgeFramework()
const serverFramework = judgeServerFramework()
if (argv.ssg) {
Expand All @@ -30,9 +30,6 @@ const startOrBuild = async (argv: Argv, type: 'start' | 'build') => {
if (!argv.api) {
const { clientPlugin } = await import(framework)
const client: IPlugin['clientPlugin'] = clientPlugin()
if (client?.name === 'plugin-react') {
await copyReactContext()
}
await client?.[type]?.(argv)
}
if (!argv.web) {
Expand Down
4 changes: 2 additions & 2 deletions packages/hoc-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
"sideEffects": false,
"scripts": {
"build": "concurrently \"tsc -p ./tsconfig.cjs.json \" \" tsc -p ./tsconfig.esm.json\"",

"watch": "concurrently \"tsc -w -p ./tsconfig.cjs.json \" \"tsc -w -p ./tsconfig.esm.json \""
},
"repository": {
Expand All @@ -39,7 +38,8 @@
"react": "^17.0.1",
"react-dom": "^17.0.0",
"react-router": "^5.2.1",
"react-router-dom": "^5.1.2"
"react-router-dom": "^5.1.2",
"ssr-common-utils": "^6.0.0"
},
"devDependencies": {
"@types/react": "^17.0.0",
Expand Down
12 changes: 6 additions & 6 deletions packages/hoc-react/src/wrapComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import * as React from 'react'
import 'react-router'
import { useContext, useEffect, useState } from 'react'
import { useContext, useEffect, useState, createElement } from 'react'
import { withRouter, RouteComponentProps } from 'react-router-dom'
import { DynamicFC, StaticFC, Action, ReactESMFetch, ReactFetch } from 'ssr-types'
import { STORE_CONTEXT } from '_build/create-context'
import { useStoreContext } from 'ssr-common-utils'

let hasRender = false

Expand All @@ -30,10 +30,10 @@ const fetchAndDispatch = async ({ fetch, layoutFetch }: fetchType, dispatch: Rea
payload: combineData
})
}
function wrapComponent (WrappedComponent: DynamicFC|StaticFC) {
return withRouter((props) => {
function wrapComponent(WrappedComponent: DynamicFC | StaticFC) {
return withRouter((props: any) => {
const [ready, setReady] = useState(WrappedComponent.name !== 'dynamicComponent')
const { state, dispatch } = useContext(STORE_CONTEXT)
const { state, dispatch } = useContext(useStoreContext() as any)

useEffect(() => {
didMount()
Expand All @@ -55,7 +55,7 @@ function wrapComponent (WrappedComponent: DynamicFC|StaticFC) {
hasRender = true
}
return (
ready ? <WrappedComponent {...props}></WrappedComponent> : null
ready ? createElement(WrappedComponent, { ...props }) : null
)
})
}
Expand Down
4 changes: 2 additions & 2 deletions packages/hoc-react18/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
"sideEffects": false,
"scripts": {
"build": "concurrently \"tsc -p ./tsconfig.cjs.json \" \" tsc -p ./tsconfig.esm.json\"",

"watch": "concurrently \"tsc -w -p ./tsconfig.cjs.json \" \"tsc -w -p ./tsconfig.esm.json \""
},
"repository": {
Expand All @@ -39,7 +38,8 @@
"react": "^18.0.1",
"react-dom": "^18.0.0",
"react-router": "^5.2.1",
"react-router-dom": "^5.1.2"
"react-router-dom": "^5.1.2",
"ssr-common-utils": "^6.0.0"
},
"devDependencies": {
"@types/react": "^18.0.0",
Expand Down
6 changes: 3 additions & 3 deletions packages/hoc-react18/src/wrapComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import 'react-router'
import { createElement, useContext, useEffect, useState } from 'react'
import { withRouter, RouteComponentProps } from 'react-router-dom'
import { DynamicFC, StaticFC, Action, ReactESMFetch, ReactFetch } from 'ssr-types'
import { STORE_CONTEXT } from '_build/create-context'
import { useStoreContext } from 'ssr-common-utils'

let hasRender = false

Expand All @@ -30,10 +30,10 @@ const fetchAndDispatch = async ({ fetch, layoutFetch }: fetchType, dispatch: Rea
payload: combineData
})
}
function wrapComponent (WrappedComponent: DynamicFC|StaticFC) {
function wrapComponent(WrappedComponent: DynamicFC | StaticFC) {
return withRouter((props: any) => {
const [ready, setReady] = useState(WrappedComponent.name !== 'dynamicComponent')
const { state, dispatch } = useContext(STORE_CONTEXT as any)
const { state, dispatch } = useContext(useStoreContext() as any)

useEffect(() => {
didMount()
Expand Down
10 changes: 4 additions & 6 deletions packages/plugin-react/src/entry/client-entry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,24 @@ import { BrowserRouter, Route, Switch } from 'react-router-dom'
import { preloadComponent, isMicro, setStoreContext, setStore } from 'ssr-common-utils'
import { wrapComponent } from 'ssr-hoc-react'
import { LayoutProps } from 'ssr-types'
import { STORE_CONTEXT as Context } from '_build/create-context'
import { Routes } from './create-router'
import { createStore } from './create-store'
import { ssrCreateContext, Routes, createStore } from './create'
import { AppContext } from './context'

const { FeRoutes, layoutFetch, App } = Routes

const clientRender = async (): Promise<void> => {
const IApp = App ?? function (props: LayoutProps) {
const IApp = App ?? function(props: LayoutProps) {
return props.children!
}
setStoreContext(Context)
setStoreContext(ssrCreateContext() as any)
const store = createStore(window.__VALTIO_DATA__)
setStore(store ?? {})
const baseName = isMicro() ? window.clientPrefix : window.prefix
const routes = await preloadComponent(FeRoutes, baseName)
ReactDOM[window.__USE_SSR__ ? 'hydrate' : 'render'](
createElement(BrowserRouter, {
basename: baseName
}, createElement(AppContext as any, {
}, createElement(AppContext, {
children: createElement(Switch, null,
createElement(IApp as any, null, createElement(Switch, null,
routes.map(item => {
Expand Down
18 changes: 9 additions & 9 deletions packages/plugin-react/src/entry/context.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
import { useReducer, createElement } from 'react'
import { IProps, Action, IWindow, ReactRoutesType } from 'ssr-types'
import { STORE_CONTEXT as Context } from '_build/create-context'
import { Routes } from './create-router'
import { ssrCreateContext, Routes } from './create'

const { reducer, state } = Routes as ReactRoutesType

const userState = state ?? {}
const userReducer = reducer ?? function () {}
const userReducer = reducer ?? function() { }

const isDev = process.env.NODE_ENV !== 'production'

// 客户端的 context 只需要创建一次,在页面整个生命周期内共享
declare const window: IWindow

function defaultReducer (state: any, action: Action) {
function defaultReducer(state: any, action: Action) {
switch (action.type) {
case 'updateContext':
if (isDev) {
Expand All @@ -24,17 +22,19 @@ function defaultReducer (state: any, action: Action) {
}
}

const initialState = Object.assign({}, userState ?? {}, window.__INITIAL_DATA__)

function combineReducer (state: any, action: any) {
function combineReducer(state: any, action: any) {
return defaultReducer(state, action) || userReducer(state, action)
}
export function AppContext (props: IProps) {

export function AppContext(props: IProps) {
const initialState = Object.assign({}, userState ?? {}, __isBrowser__ ? window?.__INITIAL_DATA__ : props.initialState)
const Context = ssrCreateContext()
const [state, dispatch] = useReducer(combineReducer, initialState)
return createElement(Context.Provider, {
value: {
state,
dispatch
}
}, props.children)
}, props.children as any)
}
19 changes: 0 additions & 19 deletions packages/plugin-react/src/entry/create-context.ts

This file was deleted.

10 changes: 0 additions & 10 deletions packages/plugin-react/src/entry/create-router.ts

This file was deleted.

13 changes: 0 additions & 13 deletions packages/plugin-react/src/entry/create-store.tsx

This file was deleted.

37 changes: 37 additions & 0 deletions packages/plugin-react/src/entry/create.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { createContext } from 'react'
import type { Context } from 'react'
import type { IContext } from 'ssr-types'
import { proxy } from 'valtio'
import { deepClone } from 'ssr-deepclone'
import { combineRoutes } from 'ssr-common-utils'
import * as declareRoutes from '_build/ssr-declare-routes'
import * as ManualRoutes from '_build/ssr-manual-routes'
import { ReactRoutesType } from 'ssr-types'

export const Routes = combineRoutes(declareRoutes, ManualRoutes) as ReactRoutesType

export const ssrCreateContext = () => {
let STORE_CONTEXT: Context<IContext>

if (__isBrowser__) {
STORE_CONTEXT = window.STORE_CONTEXT || createContext<IContext>({
state: {}
})
window.STORE_CONTEXT = STORE_CONTEXT
return STORE_CONTEXT
} else {
STORE_CONTEXT = createContext<IContext>({
state: {}
})
}
return STORE_CONTEXT
}

export function createStore (initialData?: any) {
const { store } = Routes
const storeInstance = initialData ? store : deepClone(store)
for (const key in storeInstance) {
storeInstance[key] = initialData ? proxy(initialData[key]) : proxy(storeInstance[key])
}
return storeInstance
}
27 changes: 13 additions & 14 deletions packages/plugin-react/src/entry/server-entry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import { renderToString, renderToNodeStream } from 'react-dom/server'
import { findRoute, getManifest, logGreen, normalizePath, getAsyncCssChunk, getAsyncJsChunk, splitPageInfo, reactRefreshFragment, localStorageWrapper, checkRoute, useStore } from 'ssr-common-utils'
import { ISSRContext, IConfig, ReactESMPreloadFeRouteItem, DynamicFC, StaticFC } from 'ssr-types'
import { serialize } from 'ssr-serialize-javascript'
import { STORE_CONTEXT as Context } from '_build/create-context'
import { Routes } from './create-router'
import { createStore } from './create-store'
import { AppContext } from './context'
import { Routes, ssrCreateContext, createStore } from './create'

const { FeRoutes, layoutFetch, state, Layout } = Routes

const serverRender = async (ctx: ISSRContext, config: IConfig) => {
const context = ssrCreateContext()
const { mode, parallelFetch, prefix, isVite, isDev, clientPrefix, stream, rootId, hashRouter } = config
const rawPath = ctx.request.path ?? ctx.request.url
const path = normalizePath(rawPath, prefix)
Expand Down Expand Up @@ -80,22 +80,21 @@ const serverRender = async (ctx: ISSRContext, config: IConfig) => {
const ele = createElement(StaticRouter, {
location: ctx.request.url,
basename: prefix === '/' ? undefined : prefix
}, createElement(Context.Provider, {
value: {
state: combineData
}
}, createElement(Layout, {
ctx: ctx,
config: config,
staticList: staticList,
injectState: injectState
}, createElement(Component, null))))
}, createElement(AppContext, {
initialState: combineData,
children: createElement(Layout, {
ctx: ctx,
config: config,
staticList: staticList,
injectState: injectState
}, createElement(Component, null))
}))
// for ctx.body will loose asynclocalstorage context, consume stream in advance like vue2/3
return stream ? renderToNodeStream(ele).pipe(new PassThrough()) : renderToString(ele)
}

return await localStorageWrapper.run({
context: Context,
context: context as any,
ctx,
store: createStore()
}, fn)
Expand Down
8 changes: 3 additions & 5 deletions packages/plugin-react18/src/entry/client-entry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,16 @@ import { BrowserRouter, Route, Switch } from 'react-router-dom'
import { preloadComponent, isMicro, setStoreContext, setStore } from 'ssr-common-utils'
import { wrapComponent } from 'ssr-hoc-react18'
import { LayoutProps } from 'ssr-types'
import { STORE_CONTEXT as Context } from '_build/create-context'
import { Routes } from './create-router'
import { createStore } from './create-store'
import { ssrCreateContext, Routes, createStore } from './create'
import { AppContext } from './context'

const { FeRoutes, layoutFetch, App } = Routes

const clientRender = async (): Promise<void> => {
const IApp = App ?? function (props: LayoutProps) {
const IApp = App ?? function(props: LayoutProps) {
return props.children!
}
setStoreContext(Context)
setStoreContext(ssrCreateContext() as any)
const store = createStore(window.__VALTIO_DATA__)
setStore(store ?? {})
const baseName = isMicro() ? window.clientPrefix : window.prefix
Expand Down

0 comments on commit 1d3e885

Please sign in to comment.