Isomorphic-style-loader, не может прочитать свойство 'apply' из null
Привет, я видел эту же ошибку и несколько возможных решений, но ни одно из них не смогло решить мою проблему (возможно, потому что мне не хватает глубокого понимания всей структуры React).
Я знаю, что context.insertCss.apply(context, styles); не получает контекст, и поэтому выдается ошибка, я добавил ContextProvider, но боюсь, что это может конфликтовать с настройкой маршрутизации. Также использовал ответ Даниэля на этот вопрос [ Почему загрузчик isomorphic-style-loader генерирует TypeError: Невозможно прочитать свойство 'apply' undefined при использовании в унисон с CSS-модулями
сервер 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>`
)
);
});
});
});
На сервере добавлен ContextProvider в методе renderToString(..), также я заменяю тело 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
Использовал контекстный провайдер из ответа Даниила (ссылка выше)
Клиент 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, как предполагается.
App.js, используемый внутри ContextProvider
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);
route.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;
Почти наверняка есть легкое решение этой проблемы, но это не кажется мне тривиальным. Заранее спасибо.
0 ответов
Пожалуйста, попробуйте использовать встроенный StyleContext изоморфного загрузчика стилей вместо поставщика собственного контекста.
server.js:
import StyleContext from 'isomorphic-style-loader/StyleContext';
const insertCss = (...styles) => {
const removeCss = styles.map(style => style._insertCss());
return () => removeCss.forEach(dispose => dispose());
};
ReactDOM.render(
<StyleContext.Provider value={{ insertCss }}>
<Router>{renderRoutes(Routes)}</Router>
</StyleContext.Provider>,
document.getElementById('root')
);
client.js:
app.get('/*', function(req, res) {
const context = {};
const css = new Set(); // CSS for all rendered React components
const insertCss = (...styles) => styles.forEach(style => css.add(style._getCss()));
const component = ReactDOMServer.renderToString(
<StyleContext.Provider value={{ insertCss }}>
<StaticRouter location={req.url} context={context}>
{renderRoutes(Routes)}
</StaticRouter>
</StyleContext.Provider>
);
if (context.url) {
res.writeHead(301, { Location: context.url });
res.end();
} else {
res.send(Html('React SSR', component));
}
});
Вы можете увидеть пример проекта здесь: https://github.com/digz6666/webpack-loader-test/tree/ssr-2