使用 redux 的服务器端呈现和 MapStateToProp 内容上的 immutable.js 错误
Server-side rendering with redux and immutable.js errors on MapStateToProp contents
我目前正在使用 react v0.14
、redux v3.0
、immutable v3.7.6
进行服务器渲染,但我 运行 遇到了一些问题。每当我转到包含 mapStateToProps
的应用程序页面时,我都会在控制台中收到一条错误消息,具体取决于 Uncaught TypeError: e.dashboard.shoppingCart.get is not a function
之类的状态部分。
dashboard.shoppingCart.get
是我在 mapStateToProps
中设置的状态值,.get()
指的是 [=24= 上的 immutable.js
方法].我不确定是什么导致了这个错误,它杀死了应用程序中的任何 javascript,没有任何工作。
服务器
import http from 'http';
import React from 'react';
import {renderToString} from 'react-dom/server';
import { match, RoutingContext } from 'react-router';
import {Provider} from 'react-redux';
import configureStore from './../common/store/store.js';
import fs from 'fs';
import { createPage, write, writeError, writeNotFound, redirect } from './server-utils.js';
import routes from './../common/routes/rootRoutes.js';
const PORT = process.env.PORT || 4000;
function renderApp(props, res) {
var store = configureStore();
var markup = renderToString(
<Provider store={store}>
<RoutingContext {...props}/>
</Provider>
);
const initialState = store.getState();
var html = createPage(markup, initialState);
write(html, 'text/html', res);
}
http.createServer((req, res) => {
if (req.url === '/favicon.ico') {
write('haha', 'text/plain', res);
}
// serve JavaScript assets
else if (/__build__/.test(req.url)) {
fs.readFile(`.${req.url}`, (err, data) => {
write(data, 'text/javaScript', res);
})
}
// handle all other urls with React Router
else {
match({ routes, location: req.url }, (error, redirectLocation, renderProps) => {
if (error)
writeError('ERROR!', res);
else if (redirectLocation)
redirect(redirectLocation, res);
else if (renderProps)
renderApp(renderProps, res);
else
writeNotFound(res);
});
}
}).listen(PORT)
console.log(`listening on port ${PORT}`)
客户
import React from 'react';
import { match, Router } from 'react-router';
import { render } from 'react-dom';
import { createHistory } from 'history';
import routes from './../common/routes/rootRoutes.js';
import {Provider} from 'react-redux';
import configureStore from './../common/store/store.js';
import './../common/styles/main.scss';
const { pathname, search, hash } = window.location;
const location = `${pathname}${search}${hash}`;
const initialState = window.__INITIAL_STATE__;
const store = configureStore(initialState);
// calling `match` is simply for side effects of
// loading route/component code for the initial location
match({ routes, location }, () => {
render(
<Provider store={store}>
<Router routes={routes} history={createHistory()} />
</Provider>,
document.getElementById('app')
);
});
DashCart
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import DashCartItem from './DashCartItem.jsx';
import * as DashCartActions from './../../actionCreators/Dashboard/DashShoppingCart.js';
function DashCart (props) {
var DashCartItemsList = props.cartSneakers.map((sneaker, key ) => {
return <DashCartItem sneaker={sneaker} key={key} remove={props.actions.removeSneakerFromCart}></DashCartItem>;
});
var price = () => {
var prices = [];
props.cartSneakers.map((sneaker) => prices.push(sneaker.get('price')));
var result = prices.reduce((sneakerOne, sneakerTwo) => sneakerOne + sneakerTwo, 0);
return (result !== 0) ? 'Estimated Total: $' + result : 'Cart is Empty!';
}
var totalList = props.cartSneakers.map((sneaker, key) => {
return <div key={key}><h4 className="sneaker">{sneaker.get('sneakerName')}</h4> <h4 className="price"> ${sneaker.get('price')}</h4></div>
})
var checkOut = () => props.actions.checkout(props.cartSneakers);
return (
<div className="DashCart">
<div className="col-sm-9 segment nopadding">
{DashCartItemsList}
</div>
<div className="col-sm-3 nopadding checkOut">
<div className="panel panel-default">
<div className="panel-heading">
<h4 className="panel-title">Cart Estimated Subtotal</h4>
</div>
{totalList}
<hr></hr>
<h4 className="total">{price()}</h4>
<h4 className="total"></h4>
</div>
</div>
<button onClick={checkOut} className="checkout btn btn-default">CheckOut</button>
</div>
);
}
function mapStateToProps(state) {
return {
cartSneakers: state.dashboard.shoppingCart.get('cartSneakers')
}
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(DashCartActions, dispatch)
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(DashCart);
仪表板减速器
export const dashboardReducer = combineReducers({
userSneakers: dashSharedReducer,
shoppingCart: dashShoppingCartReducer,
trades: dashTradesReducer,
orderHistory: dashOrderHistoryReducer,
events: dashEventsReducer,
sneakerTracking: dashSneakerTrackingReducer,
shippingInfo: dashShippingReducer,
billingInfo: dashBillingReducer,
accountInfo: dashAccountSettingsReducer
});
//ShoppingCart Reducer
export function dashShoppingCartReducer (state = sample, action ) {
switch (action.type) {
case CHECKOUT:
return handleCheckout(state, action.cartPayload);
case REMOVE_SNEAKER_FROM_CART:
return handleRemoveSneakerFromCart(state, action.sneakerToRemove);
case RECEIVE_SNEAKERS_IN_CART:
return handleReceiveSneakersInCart(state, action.cartSneakers);
default:
return state;
}
}
我找到了解决问题的办法。我的应用程序的服务器实现自动强制 immutable
数据结构,这导致 get()
无法识别并抛出一个错误,导致应用程序中的所有 Javascript 停止。
要解决这个问题,您必须确保在服务器和客户端上调用 getState()
之后将状态转换为不可变数据结构,然后再将其传递给 configureStore()
。为此,只需将以下行添加到服务器和客户端文件中:
var state = I.fromJS(initialState)
并将其传递到商店 configureStore(state)
。
我目前正在使用 react v0.14
、redux v3.0
、immutable v3.7.6
进行服务器渲染,但我 运行 遇到了一些问题。每当我转到包含 mapStateToProps
的应用程序页面时,我都会在控制台中收到一条错误消息,具体取决于 Uncaught TypeError: e.dashboard.shoppingCart.get is not a function
之类的状态部分。
dashboard.shoppingCart.get
是我在 mapStateToProps
中设置的状态值,.get()
指的是 [=24= 上的 immutable.js
方法].我不确定是什么导致了这个错误,它杀死了应用程序中的任何 javascript,没有任何工作。
服务器
import http from 'http';
import React from 'react';
import {renderToString} from 'react-dom/server';
import { match, RoutingContext } from 'react-router';
import {Provider} from 'react-redux';
import configureStore from './../common/store/store.js';
import fs from 'fs';
import { createPage, write, writeError, writeNotFound, redirect } from './server-utils.js';
import routes from './../common/routes/rootRoutes.js';
const PORT = process.env.PORT || 4000;
function renderApp(props, res) {
var store = configureStore();
var markup = renderToString(
<Provider store={store}>
<RoutingContext {...props}/>
</Provider>
);
const initialState = store.getState();
var html = createPage(markup, initialState);
write(html, 'text/html', res);
}
http.createServer((req, res) => {
if (req.url === '/favicon.ico') {
write('haha', 'text/plain', res);
}
// serve JavaScript assets
else if (/__build__/.test(req.url)) {
fs.readFile(`.${req.url}`, (err, data) => {
write(data, 'text/javaScript', res);
})
}
// handle all other urls with React Router
else {
match({ routes, location: req.url }, (error, redirectLocation, renderProps) => {
if (error)
writeError('ERROR!', res);
else if (redirectLocation)
redirect(redirectLocation, res);
else if (renderProps)
renderApp(renderProps, res);
else
writeNotFound(res);
});
}
}).listen(PORT)
console.log(`listening on port ${PORT}`)
客户
import React from 'react';
import { match, Router } from 'react-router';
import { render } from 'react-dom';
import { createHistory } from 'history';
import routes from './../common/routes/rootRoutes.js';
import {Provider} from 'react-redux';
import configureStore from './../common/store/store.js';
import './../common/styles/main.scss';
const { pathname, search, hash } = window.location;
const location = `${pathname}${search}${hash}`;
const initialState = window.__INITIAL_STATE__;
const store = configureStore(initialState);
// calling `match` is simply for side effects of
// loading route/component code for the initial location
match({ routes, location }, () => {
render(
<Provider store={store}>
<Router routes={routes} history={createHistory()} />
</Provider>,
document.getElementById('app')
);
});
DashCart
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import DashCartItem from './DashCartItem.jsx';
import * as DashCartActions from './../../actionCreators/Dashboard/DashShoppingCart.js';
function DashCart (props) {
var DashCartItemsList = props.cartSneakers.map((sneaker, key ) => {
return <DashCartItem sneaker={sneaker} key={key} remove={props.actions.removeSneakerFromCart}></DashCartItem>;
});
var price = () => {
var prices = [];
props.cartSneakers.map((sneaker) => prices.push(sneaker.get('price')));
var result = prices.reduce((sneakerOne, sneakerTwo) => sneakerOne + sneakerTwo, 0);
return (result !== 0) ? 'Estimated Total: $' + result : 'Cart is Empty!';
}
var totalList = props.cartSneakers.map((sneaker, key) => {
return <div key={key}><h4 className="sneaker">{sneaker.get('sneakerName')}</h4> <h4 className="price"> ${sneaker.get('price')}</h4></div>
})
var checkOut = () => props.actions.checkout(props.cartSneakers);
return (
<div className="DashCart">
<div className="col-sm-9 segment nopadding">
{DashCartItemsList}
</div>
<div className="col-sm-3 nopadding checkOut">
<div className="panel panel-default">
<div className="panel-heading">
<h4 className="panel-title">Cart Estimated Subtotal</h4>
</div>
{totalList}
<hr></hr>
<h4 className="total">{price()}</h4>
<h4 className="total"></h4>
</div>
</div>
<button onClick={checkOut} className="checkout btn btn-default">CheckOut</button>
</div>
);
}
function mapStateToProps(state) {
return {
cartSneakers: state.dashboard.shoppingCart.get('cartSneakers')
}
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(DashCartActions, dispatch)
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(DashCart);
仪表板减速器
export const dashboardReducer = combineReducers({
userSneakers: dashSharedReducer,
shoppingCart: dashShoppingCartReducer,
trades: dashTradesReducer,
orderHistory: dashOrderHistoryReducer,
events: dashEventsReducer,
sneakerTracking: dashSneakerTrackingReducer,
shippingInfo: dashShippingReducer,
billingInfo: dashBillingReducer,
accountInfo: dashAccountSettingsReducer
});
//ShoppingCart Reducer
export function dashShoppingCartReducer (state = sample, action ) {
switch (action.type) {
case CHECKOUT:
return handleCheckout(state, action.cartPayload);
case REMOVE_SNEAKER_FROM_CART:
return handleRemoveSneakerFromCart(state, action.sneakerToRemove);
case RECEIVE_SNEAKERS_IN_CART:
return handleReceiveSneakersInCart(state, action.cartSneakers);
default:
return state;
}
}
我找到了解决问题的办法。我的应用程序的服务器实现自动强制 immutable
数据结构,这导致 get()
无法识别并抛出一个错误,导致应用程序中的所有 Javascript 停止。
要解决这个问题,您必须确保在服务器和客户端上调用 getState()
之后将状态转换为不可变数据结构,然后再将其传递给 configureStore()
。为此,只需将以下行添加到服务器和客户端文件中:
var state = I.fromJS(initialState)
并将其传递到商店 configureStore(state)
。