Redux 和 React-Router:动态路由器不工作
Posted
技术标签:
【中文标题】Redux 和 React-Router:动态路由器不工作【英文标题】:Redux & React-Router: Dynamic Router Not Working 【发布时间】:2016-03-16 10:39:55 【问题描述】:这在 Alt.js 上运行良好,然后我切换到了 redux。我真的很难把头绕在 redux 上。
错误:Invariant Violation:对象作为 React 子项无效(发现:对象带有键 id, doc…
我正在使用:
"react": "^0.14.3",
"react-dom": "^0.14.3",
"react-redux": "^4.0.0",
"react-router": "^1.0.2",
"redux": "^3.0.4"
任何人都可以看到为什么我收到错误并且没有显示任何组件吗?
import React, Component, PropTypes from 'react';
import Router, Route from 'react-router';
// redux
import connect from 'react-redux';
import fetchNavItemsIfNeeded from '../redux/actions/nav-item-actions';
class Routes extends Component
constructor(props)
super(props);
componentWillMount()
const dispatch = this.props;
dispatch(fetchNavItemsIfNeeded('7B3E7eWWPizd11n'));
fetchMenuSystem(data)
const self = this;
const currRoutesState = this.props.navItems;
const routes = data === undefined ? this.props.navItems : data;
routes.map((route) =>
// set paths up first
let currPaths = [];
if (route.paths !== undefined)
currPaths = route.paths;
else
currPaths.push(route.linkTo);
// Components - first check for ecomMods
let currComponent;
if (route.ecomMod !== undefined)
currComponent = require('../components/eCommerce/' + (route.ecomMod).toLowerCase());
// clear out currPath if this is an ecom Module
// and start a new currPaths array
currPaths = [];
if (route.parentId === null)
currPaths.push(route.ecomMod);
else
currPaths.push(route.ecomMod + '/:id');
else
currComponent = require('../components/pages/');
currPaths.map((currPath) =>
const props = key: currPath, path: currPath, component: currComponent ;
currRoutesState.push(<Route ...props />);
);
if (route.childNodes !== undefined)
self.fetchMenuSystem(route.childNodes);
);
return currRoutesState;
fetchRoutes()
const result = this.fetchMenuSystem();
const clientId = this.props.clientId;
return (
<Route clientId= clientId component= require('../components/APP') >
result
<Route path="*" component= require('../components/pages/Not-Found') />
</Route>
);
render()
if (!this.props.navItems) return <div>Loading ...</div>;
const routerProps =
routes: this.fetchRoutes(),
createElement: (component, props) =>
return React.createElement(component, ...props );
;
return (
<div>
<Router ...routerProps history= this.props.history />
</div>
);
Routes.propTypes =
clientId: PropTypes.string.isRequired,
dispatch: PropTypes.func.isRequired,
error: PropTypes.object,
history: PropTypes.object.isRequired,
navItems: PropTypes.array.isRequired
;
function mapStateToProps(state)
const navItemsPerClient = state;
if (!navItemsPerClient)
return
navItems: []
;
return
navItems: navItemsPerClient.navItems
;
export default connect(mapStateToProps)(Routes);
我知道很多代码,但是数据显示出来了,而且确实是 result in this.fetchRoutes() 这给了我问题。没有它 一切正常,但当然有 95% 的路线。
【问题讨论】:
【参考方案1】:如果有人感兴趣,这里有一个带有 redux 的动态反应路由器。 [世界上有太多的待办事项示例,如果我不是总是想出真实世界示例的人,那就太好了。]
index.js
import 'babel-core/polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import Routes from './router/routes';
// redux
import Provider from 'react-redux';
import configureStore from './store/configureStore';
// styles
import './index.css';
// Setting up entire state 'schema' at inception
const store = configureStore();
ReactDOM.render(
<Provider store= store >
<Routes />
</Provider>,
document.getElementById('root')
);
routes.js
import React, Component, PropTypes from 'react';
import Router, Route, IndexRoute from 'react-router';
// redux
import connect from 'react-redux';
import fetchNavItemsIfNeeded from '../actions/nav-items-actions';
// history
import createBrowserHistory from 'history/lib/createBrowserHistory';
const history = createBrowserHistory();
import App from '../containers/app/App';
import Home from '../containers/home/Home';
import NotFound from '../containers/misc/NotFound';
class Routes extends Component
constructor()
super();
this.state =
routes: []
;
fetchMenuSystem(data)
const self = this;
const currRoutesState = this.state.routes;
const routes = data === undefined ? this.props.navItems : data;
routes.map((route) =>
// set paths up first
let currPaths = [];
if (route.paths !== undefined)
currPaths = route.paths;
else
currPaths.push(route.linkTo);
// Components - first check for ecomMods
let currComponent;
if (route.ecomMod !== undefined)
currComponent = require('../containers/' + route.ecomMod);
// clear out currPath if this is an ecom Module
// and start a new currPaths array
currPaths = [];
if (route.parentId === null)
currPaths.push(route.ecomMod);
else
currPaths.push(route.ecomMod + '/:id');
else
currComponent = require('../containers/' + route.component);
currPaths.map((currPath, idx) =>
const props = key: idx, path: currPath, component: currComponent ;
currRoutesState.push(<Route ...props />);
);
if (route.childNodes !== undefined)
self.fetchMenuSystem(route.childNodes);
);
return currRoutesState;
componentDidMount()
const dispatch = this.props;
const clientId = '7B3E7eWWPizd11n';
dispatch(fetchNavItemsIfNeeded(clientId));
render()
if (!this.props.navItems) return <div>Loading ...</div>;
return (
<Router history= history >
<Route path="/" component= App >
<IndexRoute component= Home />
this.fetchMenuSystem()
<Route path="*" component= NotFound />
</Route>
</Router>
);
function mapStateToProps(state)
const navItemsPerClient = state;
if (!navItemsPerClient)
return
isFetching: false,
didInvalidate: false,
navItems: [],
error: null
;
return
error: navItemsPerClient.error,
isFetching: navItemsPerClient.isFetching,
didInvalidate: navItemsPerClient.didInvalidate,
navItems: navItemsPerClient.navItems
;
Routes.propTypes =
dispatch: PropTypes.func.isRequired,
navItems: PropTypes.array
;
export default connect(mapStateToProps)(Routes);
nav-items-actions.js
import 'isomorphic-fetch';
import checkStatus, parseJSON from './utils';
export const INVALIDATE_NAV_ITEMS = 'INVALIDATE_NAV_ITEMS';
export const NAV_ITEMS_REQUEST = 'NAV_ITEMS_REQUEST';
export const NAV_ITEMS_SUCCESS = 'NAV_ITEMS_SUCCESS';
export const NAV_ITEMS_FAILURE = 'NAV_ITEMS_FAILURE';
export function invalidateNavItems()
return
type: INVALIDATE_NAV_ITEMS
;
function navItemsRequest()
return
type: NAV_ITEMS_REQUEST
;
function navItemsSuccess(payload)
return
type: NAV_ITEMS_SUCCESS,
navItems: payload.navItems
;
function navItemsFailure(error)
return
type: NAV_ITEMS_FAILURE,
error
;
export function fetchNavItems(clientId)
const API_URL = (`../data/$clientId/navigation/navigation.json`);
return dispatch =>
dispatch(navItemsRequest());
return fetch(API_URL)
.then(checkStatus)
.then(parseJSON)
.then(json => dispatch(navItemsSuccess(json)))
.catch(function(error)
const response = error.response;
if (response === undefined)
dispatch(navItemsFailure(error));
else
parseJSON(response)
.then(function(json)
error.status = response.status;
error.statusText = response.statusText;
error.message = json.message;
dispatch(navItemsFailure(error));
);
);
;
function shouldFetchNavItems(state)
// Check cache first
const navItems = state.navItemsPerClient;
if (!navItems || navItems.length === undefined)
// Not cached, should fetch
return true;
if (navItems.isFetching)
// Shouldn't fetch since fetching is running
return false;
// Should fetch if cache was invalidate
return navItems.didInvalidate;
export function fetchNavItemsIfNeeded(clientId)
return (dispatch, getState) =>
if (shouldFetchNavItems(getState()))
return dispatch(fetchNavItems(clientId));
;
utils.js
export function checkStatus(response)
if (!response.ok) // (response.status < 200 || response.status > 300)
const error = new Error(response.statusText);
error.response = response;
throw error;
return response;
export function parseJSON(response)
return response.json();
nav-items-reducer.js
import
INVALIDATE_NAV_ITEMS, NAV_ITEMS_REQUEST,
NAV_ITEMS_SUCCESS, NAV_ITEMS_FAILURE
from '../actions/nav-items-actions';
function navItems(state =
isFetching: false,
didInvalidate: false,
navItems: [],
error: null
, action)
switch (action.type)
case INVALIDATE_NAV_ITEMS:
return Object.assign(, state,
didInvalidate: true
);
case NAV_ITEMS_REQUEST:
return Object.assign(, state,
isFetching: true,
didInvalidate: false
);
case NAV_ITEMS_SUCCESS:
return Object.assign(, state,
isFetching: false,
didInvalidate: false,
navItems: action.navItems,
error: null
);
case NAV_ITEMS_FAILURE:
return Object.assign(, state,
isFetching: false,
didInvalidate: false,
error: action.error
);
default:
return state;
export function navItemsPerClient(state = , action)
switch (action.type)
case INVALIDATE_NAV_ITEMS:
case NAV_ITEMS_REQUEST:
case NAV_ITEMS_SUCCESS:
case NAV_ITEMS_FAILURE:
return navItems(state, action);
default:
return state;
configure-store.js
import createStore, applyMiddleware, combineReducers from 'redux';
import thunkMiddleware from 'redux-thunk';
import createLogger from 'redux-logger';
import auth from '../reducers/auth-reducer';
import navItemsPerClient from '../reducers/nav-items-reducer';
const logger = createLogger();
const reducer = combineReducers(
auth,
navItemsPerClient
);
const createStoreWithMiddleware = applyMiddleware(
thunkMiddleware,
logger
)(createStore);
export default function configureStore(initialState)
return createStoreWithMiddleware(reducer, initialState);
navigation.json
"incomplete_results": false,
"navItems": [
"linkTo": "/about",
"component": "about/About",
"childNodes": [
"linkTo": "/proforma",
"component": "about/ProForma"
]
,
"linkTo": "/login",
"component": "login/Login"
,
"linkTo": "/none",
"component": "misc/RestrictPage",
"childNodes": [
"linkTo": "/users",
"component": "user/UsersPage"
,
"linkTo": "/repos",
"component": "repo/ReposPage"
]
]
package.json
"dependencies":
"body-parser": "^1.14.1",
"classnames": "^2.2.0",
"express": "^4.13.3",
"fixed-data-table": "^0.5.0",
"history": "^1.13.0",
"isomorphic-fetch": "^2.1.1",
"lodash": "^3.10.1",
"react": "^0.14.3",
"react-dom": "^0.14.3",
"react-redux": "^4.0.0",
"react-router": "^1.0.2",
"redux": "^3.0.4",
"redux-logger": "^2.0.4",
"redux-thunk": "^1.0.0"
,
"devDependencies":
"babel": "^5.8.29",
"babel-core": "^5.8.33",
"babel-eslint": "^4.1.5",
"babel-loader": "^5.3.2",
"eslint": "^1.9.0",
"eslint-config-airbnb": "^1.0.0",
"eslint-loader": "^1.1.1",
"eslint-plugin-react": "^3.8.0",
"file-loader": "^0.8.4",
"raw-loader": "^0.5.1",
"redbox-react": "^1.1.1",
"rimraf": "^2.4.3",
"stats-webpack-plugin": "^0.2.2",
"style-loader": "^0.13.0",
"url-loader": "^0.5.6",
"webpack": "^1.12.4",
"webpack-dev-middleware": "^1.2.0",
"webpack-hot-middleware": "^2.4.1"
【讨论】:
【参考方案2】:在react-router
v1.0 及更高版本中,您需要这样做:
import Router, Route from 'react-router';
【讨论】:
我正在使用 react-router 1.0.2 这适用于 1.0 以上的任何版本,即 1.0.2 也是如此。以上是关于Redux 和 React-Router:动态路由器不工作的主要内容,如果未能解决你的问题,请参考以下文章
(通用 React + redux + react-router)如何避免在初始浏览器加载时重新获取路由数据?
react-router搭配react-redux无法监听路由变化的问题
在将 react-router 5 与 redux 7 一起使用时,react-router <Link> 在转到新路由后不会重置状态
如何用 redux 和 react-router 组织状态?