本文代码地址:github.com/ishanyang/m…
spreado地址:github.com/react-easie…
关于服务端渲染Server-side rendering(SSR)简述
通过前面的文章希望你对Spreado 基本用法有了足够了解( Spreado - 轻松地在 React 组件间传播状态和数据)。
本文采用next.js框架来实现react 服务端渲染 ssr (因为next.js基本可以做到0配置,即可完成ssr功能开发), swr库用于数据请求。
与常规redux的preloaded state生成过程类似,通过spreado ssr相关方法来简化preloaded state的生成(详细用法请参考 spreado#sever-side-rendering-ssr 文档地址),接下来通过简单的示例希望你能对Spreado有进一步了解。
项目初始化
npx create-next-app my-first-next-spreado-app --example with-redux-thunk --use-npm
可以从这里选择示例redux模版 github.com/vercel/next…
--example 选择with-redux-thunk模版
--use-npm 使用npm
安装swr、spreado
npm install swr spreado
接来下的目标
- next.js集成spreado
- 通过Repo例子演示:传播 swr 数据请求
- 通过Repo例子演示:传播 swr 数据请求如何实现ssr
1.集成spreado
在reducer.js添加reducer spreadoReduxReducerPack
// reducer.js
import { combineReducers } from 'redux'
+ import {
+ spreadoReduxReducerPack,
+ } from 'spreado/for-redux-swr';
import * as types from './types'
...
// COMBINED REDUCERS
const reducers = {
counter: counterReducer,
timer: timerReducer,
+ ...spreadoReduxReducerPack
}
在pages/_app.js集成spreado
+ import {useMemo} from 'react';
+ import {SpreadoSetupProvider} from 'spreado';
+ import {
+ SpreadoSetupForReduxSwr,
+ } from 'spreado/for-redux-swr';
import { Provider } from 'react-redux'
import { useStore } from '../store'
export default function App({ Component, pageProps }) {
const store = useStore(pageProps.initialReduxState)
+ const spreadoSetup = useMemo(() => new SpreadoSetupForReduxSwr({store}), [store]);
return (
<Provider store={store}>
+ <SpreadoSetupProvider setup={spreadoSetup}>
<Component {...pageProps} />
+ </SpreadoSetupProvider>
</Provider>
)
}
至此集成spreado已完成。
2.通过Repo例子演示:传播 swr 数据请求。
在components目录新增文件repo.js、repo-share.js
repo.js
import { useState } from 'react'
import { useSpreadIn, useSpreadOut } from 'spreado'
import { useSwrFallbackData } from 'spreado/for-redux-swr'
import useSWR from 'swr'
export const INDEX_OF_REPO_SWR = 'INDEX_OF_REPO_SWR'
export function useRepoSwrSpreadOut(repoName) {
return useSpreadOut(
INDEX_OF_REPO_SWR,
useSWR([INDEX_OF_REPO_SWR, repoName], () => fetchRepoInfo(repoName), {
fallbackData: useSwrFallbackData(INDEX_OF_REPO_SWR),
})
)
}
export function useRepoSwrSpreadIn() {
return useSpreadIn(INDEX_OF_REPO_SWR, {})
}
export async function fetchRepoInfo(repoName) {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
name: repoName,
description: `Mocked description ${Math.random()}`,
subscribers_count: Math.round(1000 + 9000 * Math.random()),
stargazers_count: Math.round(1000 + 9000 * Math.random()),
forks_count: Math.round(1000 + 9000 * Math.random()),
})
}, 1000 + Math.random() * 1000)
})
}
export const DEFAULT_REPO_NAME = 'reduxjs/redux'
function Repo() {
const [repoName, setRepoName] = useState(DEFAULT_REPO_NAME)
const { data } = useRepoSwrSpreadOut(repoName)
const isLoading = !data
return (
<div>
<div>
<label>Please select a repo: </label>
<select value={repoName} onChange={(e) => setRepoName(e.target.value)}>
<option>reduxjs/redux</option>
<option>vercel/swr</option>
<option>react-easier/spreado</option>
</select>
</div>
{isLoading ? (
<div>Loading...</div>
) : (
<div>
<p>Name: {data.name}</p>
<p>Description: {data.description}</p>
<p>
Numbers:
<span>👀 {data.subscribers_count}</span>
<span>✨ {data.stargazers_count}</span>
<span>🍴 {data.forks_count}</span>
</p>
</div>
)}
</div>
)
}
export default Repo
repo-share.js
import { useRepoSwrSpreadIn } from './repo'
function RepoShare() {
const { data } = useRepoSwrSpreadIn()
const isLoading = !data
return (
<div>
{isLoading ? (
<div>Loading in another format...</div>
) : (
<p>
Numbers in another format:
<span>👁 {data.subscribers_count}</span>
<span>⭐️ {data.stargazers_count}</span>
<span>⑂ {data.forks_count}</span>
</p>
)}
</div>
)
}
export default RepoShare
在首页pages/index.js引入组件Repo、RepoShare
import { useEffect } from 'react'
import { useDispatch } from 'react-redux'
import Link from 'next/link'
import { startClock } from '../actions'
import Examples from '../components/examples'
+ import Repo from '../components/repo';
+ import RepoShare from '../components/repo-share';
const Index = () => {
const dispatch = useDispatch()
useEffect(() => {
dispatch(startClock())
}, [dispatch])
return (
<>
<Examples />
<Link href="/show-redux-state">
<a>Click to see current Redux State</a>
</Link>
+ <section>
+ <h2>repo info</h2>
+ <Repo />
+ <RepoShare />
+ </section>
</>
)
}
export default Index
首次渲染RepoShare RepoShare两个组件均为loading状态(此时并没有实现ssr)
在短暂的数据拉取延迟后RepoShare RepoShare两个组件均正常显示
3.通过Repo例子演示:传播 swr 数据请求如何实现ssr
关键点:
- 通过
next.js服务端方法 getServerSideProps() 动态获取相关初始值 - 通过
spreado/for-redux-swrrenderSwrResponse() 方法加工数据 - 通过
spreado/for-redux-swrcreateSpreadoReduxPreloadedState方法生成spread redux preloaded state数据
在首页pages/index.js新增服务端方法getServerSideProps() 用于repoInfo数据的获取
export async function getServerSideProps() {
const repoInfo = await fetchRepoInfo(DEFAULT_REPO_NAME)
return {
props: {
initialReduxState: createSpreadoReduxPreloadedState({
[INDEX_OF_REPO_SWR]: renderSwrResponse(repoInfo)
}),
}
}
}
首页pages/index.js最终代码如下:
import { useEffect } from 'react'
import { useDispatch } from 'react-redux'
import Link from 'next/link'
+ import {
+ createSpreadoReduxPreloadedState,
+ renderSwrResponse,
+ } from 'spreado/for-redux-swr'
import { startClock } from '../actions'
import Examples from '../components/examples'
+ import Repo, {
+ DEFAULT_REPO_NAME,
+ fetchRepoInfo,
+ INDEX_OF_REPO_SWR
+ } from '../components/repo';
import RepoShare from '../components/repo-share';
+ export async function getServerSideProps() {
+ const repoInfo = await fetchRepoInfo(DEFAULT_REPO_NAME)
+
+ return {
+ props: {
+ initialReduxState: createSpreadoReduxPreloadedState({
+ [INDEX_OF_REPO_SWR]: renderSwrResponse(repoInfo)
+ }),
+ }
+ }
+ }
const Index = () => {
const dispatch = useDispatch()
useEffect(() => {
dispatch(startClock())
}, [dispatch])
return (
<>
<Examples />
<Link href="/show-redux-state">
<a>Click to see current Redux State</a>
</Link>
<section>
<h2>repo info</h2>
<Repo />
<RepoShare />
</section>
</>
)
}
export default Index
此时可以看到ssr内容已正确渲染
至此传播 swr 数据请求ssr已实现。
写在最后
在组合使用 状态管理库 和 数据拉取库 时会藏有许多这样那样的小问题,而 Spreado 旨在将 React 组件间状态和数据的传播问题一网打尽,让我们可以更加专注于 React 应用逻辑。如果有任何的疑问或想法,欢迎在 spreado/issues 留言,中英文都可以。如果有时间和兴趣,欢迎直接提交代码,具体可以参考开发引导。如果觉得小工具有帮助,请给 GitHub repo react-easier/spreado 点个 ⭐️,这也是我们不断前行的动力 😃。