嗨,我已经看到了同样的错误和多种可能的解决方案,但没有一个能够解决我的问题(可能是因为我对整个 React 结构缺乏深入的了解)。
我知道 context.insertCss.apply(context, styles); 没有收到上下文,这就是引发错误的原因,我添加了 ContextProvider 但我担心这可能与我的路由设置冲突。还使用了 Daniel 对这个问题的回答 [ Why does isomorphic-style-loader throw a TypeError: Cannot read property 'apply' of undefined when being used with CSS-Modules
服务器 index.js
app.get('/*', (req, res) => {
const matchingRoutes = matchRoutes(Routes, req.url);
let promises = [];
matchingRoutes.forEach(route => {
if (route.loadData) {
promises.push(route.loadData());
}
});
// promise.then(data => {
Promise.all(promises).then(dataArr => {
// Let's add the data to the context
// const context = { data };
// const context = { dataArr };
const css = new Set()
const context = { insertCss: (...styles) => styles.forEach(style => css.add(style._getCss()))}
const app = React.renderToString(
<StaticRouter location={req.url}>
<ContextProvider context={context}>
<App/>
</ContextProvider>
</StaticRouter>
)
const indexFile = path.resolve('./build/index.html');
fs.readFile(indexFile, 'utf8', (err, indexData) => {
if (err) {
console.error('Something went wrong:', err);
return res.status(500).send('Oops, better luck next time!');
}
if (context.status === 404) {
res.status(404);
}
if (context.url) {
return res.redirect(301, context.url);
}
return res.send(
indexData
.replace('<style id="myStyle"></style>',`<style type="text/css" id="myStyle">${[...css].join('')}</style>`)
.replace('<div id="root"></div>', `<div id="root">${app}</div>`)
.replace(
'</body>',
`<script>window.__ROUTE_DATA__ = ${serialize(dataArr)}</script></body>`
)
);
});
});
});
在服务器上添加了 renderToString(..) 方法中的 ContextProvider,我还替换了 html 正文,以便将接收到的 CSS 附加到 HTML 响应中。
ContextProvider.js
import React from 'react';
import PropTypes from 'prop-types'
import App from './App'
class ContextProvider extends React.Component {
static childContextTypes = {
insertCss: PropTypes.func,
}
getChildContext() {
return {
...this.props.context
}
}
render() {
return <App {
...this.props
}
/>
}
}
export default ContextProvider
使用了 Daniel 回答中的上下文提供程序(上面的参考资料)
客户端 index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import App from './App';
import ContextProvider from './ContextProvider';
const context = {
insertCss: (...styles) => {
const removeCss = styles.map(x => x._insertCss());
return () => {
removeCss.forEach(f => f());
};
},
}
ReactDOM.hydrate(
<BrowserRouter>
<ContextProvider context={context} />
</BrowserRouter>,
document.getElementById('root')
);
按设想通过 ContextProvider 传递上下文。
ContextProvider 内部使用的 App.js
import React from 'react';
import { renderRoutes } from 'react-router-config';
import { Switch, NavLink } from 'react-router-dom';
import Routes from './routes';
export default props => {
return (
<div>
<ul>
<li>
<NavLink to="/">Home</NavLink>
</li>
<li>
<NavLink to="/todos">Todos</NavLink>
</li>
<li>
<NavLink to="/posts">Posts</NavLink>
</li>
</ul>
<Switch>
{renderRoutes(Routes)}
</Switch>
</div>
);
};
我正在尝试测试自定义样式的 Home.js
import React from 'react';
import withStyles from '../../node_modules/isomorphic-style-loader/withStyles'
import styles from '../scss/Home.scss';
function Home(props, context) {
return (
<h1>Hello, world!</h1>
)
}
export default withStyles(styles)(Home);
routes.js 描述了使用的路由。
import Home from './components/Home';
import Posts from './components/Posts';
import Todos from './components/Todos';
import NotFound from './components/NotFound';
import loadData from './helpers/loadData';
const Routes = [
{
path: '/',
exact: true,
component: Home
},
{
path: '/posts',
component: Posts,
loadData: () => loadData('posts')
},
{
path: '/todos',
component: Todos,
loadData: () => loadData('todos')
},
{
component: NotFound
}
];
export default Routes;
几乎可以肯定这个问题有一个简单的解决方案,但对我来说似乎并不那么微不足道。先感谢您。