React+Redux+Router服务端渲染:非路由组件无法获取staticContext
解决React SSR中非路由组件无法访问staticContext的问题
我之前在做React+Redux+Router的异步服务端渲染时,也踩过这个坑!其实本质原因很简单:staticContext是React Router在服务端渲染时,专门传递给直接被<Route>匹配渲染的组件的props,那些深层嵌套的组件、或者没有直接和路由绑定的组件,自然拿不到这个上下文。下面给你几个实用的解决方案,按场景选就行:
方法1:用React Context全局传递staticContext
这是最通用的方案,适合大部分场景。React Context天生就是用来跨组件传递数据的,我们可以把staticContext放到顶层Context里,让所有子组件都能轻松获取:
步骤:
- 先创建一个Context文件:
// src/contexts/StaticContext.js import { createContext, useContext } from 'react'; const StaticContext = createContext(null); export const useStaticContext = () => useContext(StaticContext); export default StaticContext;
- 在服务端渲染的入口,用Context.Provider包裹你的App组件,把
staticContext传进去:
// 服务端渲染逻辑 import StaticContext from './contexts/StaticContext'; const renderServerSide = async (req, res) => { const staticContext = {}; // 初始化你的staticContext const app = ( <StaticContext.Provider value={staticContext}> <Router location={req.url} context={staticContext}> <App /> </Router> </StaticContext.Provider> ); // 后续的渲染、数据预取等逻辑... };
- 在客户端入口也要同步包裹(客户端没有staticContext,传null即可):
// 客户端入口 import StaticContext from './contexts/StaticContext'; ReactDOM.hydrate( <StaticContext.Provider value={null}> <BrowserRouter> <App /> </BrowserRouter> </StaticContext.Provider>, document.getElementById('root') );
- 现在任何组件都可以用自定义Hook获取staticContext了:
import { useStaticContext } from './contexts/StaticContext'; const DeepNestedComponent = () => { const staticContext = useStaticContext(); // 服务端渲染时就能拿到staticContext,客户端是null,注意做判断 if (staticContext) { // 比如设置HTTP状态码 staticContext.statusCode = 404; } return <div>...</div>; };
方法2:把staticContext存到Redux Store里
既然你已经在用Redux了,也可以把staticContext放到全局状态中,这样任何组件都能通过Redux的useSelector或者connect获取:
步骤:
- 先定义一个Redux action和reducer:
// src/store/actions/staticContextActions.js export const SET_STATIC_CONTEXT = 'SET_STATIC_CONTEXT'; export const setStaticContext = (context) => ({ type: SET_STATIC_CONTEXT, payload: context, }); // src/store/reducers/staticContextReducer.js import { SET_STATIC_CONTEXT } from '../actions/staticContextActions'; const initialState = null; export default (state = initialState, action) => { switch (action.type) { case SET_STATIC_CONTEXT: return action.payload; default: return state; } };
- 在服务端渲染时,初始化store并dispatch这个action:
// 服务端渲染逻辑 import { createStore } from 'redux'; import rootReducer from './store/reducers'; import { setStaticContext } from './store/actions/staticContextActions'; const renderServerSide = async (req, res) => { const staticContext = {}; const store = createStore(rootReducer); // 把staticContext存入store store.dispatch(setStaticContext(staticContext)); const app = ( <Provider store={store}> <Router location={req.url} context={staticContext}> <App /> </Router> </Provider> ); // 后续逻辑... };
- 在组件中通过useSelector获取:
import { useSelector } from 'react-redux'; const AnyComponent = () => { const staticContext = useSelector(state => state.staticContext); if (staticContext) { // 做你需要的操作,比如设置状态码 } return <div>...</div>; };
方法3:用高阶组件(HOC)传递staticContext
如果只是少数几个组件需要访问staticContext,写个简单的HOC来传递也是个不错的选择:
// src/hocs/withStaticContext.js import React from 'react'; const withStaticContext = (WrappedComponent) => { return (props) => { // 假设这个HOC是被路由组件包裹的,路由组件会把staticContext传下来 const { staticContext, ...restProps } = props; return <WrappedComponent staticContext={staticContext} {...restProps} />; }; }; export default withStaticContext;
然后在路由组件里使用这个HOC:
// 路由组件 import withStaticContext from './hocs/withStaticContext'; import TargetComponent from './TargetComponent'; const RouteComponent = (props) => { const WrappedComponent = withStaticContext(TargetComponent); return <WrappedComponent {...props} />; }; // 路由配置 <Route path="/some-path" component={RouteComponent} />
这样TargetComponent就能通过props拿到staticContext了。
内容的提问来源于stack exchange,提问作者AvcS
相关产品推荐
相关产品推荐

