我很反应,我正在使用react-boilerplate + material-ui
我希望能够更改当前选项卡,以便更改当前路径,反之亦然。 此外,当使用路线刷新页面时,它应该转到右侧选项卡。
所以我的tabpagechooser容器组件是这样的:
index.js:
/*
*
* TabsPageChooser
*
*/
import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import { FormattedMessage } from 'react-intl';
import { createStructuredSelector } from 'reselect';
import { changeTab } from './actions';
import makeSelectTab from './selectors';
import messages from './messages';
import {Tabs, Tab} from 'material-ui/Tabs';
import FontIcon from 'material-ui/FontIcon';
export class TabsPageChooser extends React.Component { // eslint-disable-line react/prefer-stateless-function
constructor(props) {
super(props)
this.handleHome = this.props.onChangeTab.bind(null, 0);
this.handleSettings = this.props.onChangeTab.bind(null, 1);
this.handleAbout = this.props.onChangeTab.bind(null, 2);
}
render() {
console.log(this.props);
return (
<Tabs initialSelectedIndex={this.props.tab.tabIdx} >
<Tab
icon={<FontIcon className="material-icons">home</FontIcon>}
label={<FormattedMessage {...messages.home} />}
onActive={this.handleHome} />
<Tab
icon={<FontIcon className="material-icons">settings</FontIcon>}
label={<FormattedMessage {...messages.settings} />}
onActive={this.handleSettings} />
<Tab
icon={<FontIcon className="material-icons">favorite</FontIcon>}
label={<FormattedMessage {...messages.about} />}
onActive={this.handleAbout} />
</Tabs>
);
}
}
TabsPageChooser.propTypes = {
onChangeTab: React.PropTypes.func,
};
const mapStateToProps = createStructuredSelector({
tab: makeSelectTab(),
});
function mapDispatchToProps(dispatch) {
return {
onChangeTab: (tabId) => {
dispatch(changeTab(tabId));
},
};
}
export default connect(mapStateToProps, mapDispatchToProps)(TabsPageChooser);
actions.js:
/*
*
* TabsPageChooser actions
*
*/
import {
ROUTES_ID,
CHANGE_TAB,
} from './constants';
export function changeTab(tabId) {
return {
type: CHANGE_TAB,
tab: tabId,
};
}
export function urlFromId(tabId) {
if (!(tabId > 0 && tabId < ROUTES_ID)) {
return '/';
}
return ROUTES_ID[tabId];
}
export function changeTabFromUrl(url) {
console.log(url);
return changeTab(ROUTES_ID.indexOf(url));
}
constants.js:
/*
*
* TabsPageChooser constants
*
*/
export const CHANGE_TAB = 'app/TabsPageChooser/CHANGE_TAB';
export const ROUTES_ID = [
'/',
'/settings',
'/about',
];
reducer.js:
/*
*
* TabsPageChooser reducer
*
*/
import { fromJS } from 'immutable';
import {
CHANGE_TAB,
} from './constants';
const initialState = fromJS({
tabIdx: 0,
});
function tabsPageChooserReducer(state = initialState, action) {
switch (action.type) {
case CHANGE_TAB:
return state.set('tabIdx', action.tab);
default:
return state;
}
}
export default tabsPageChooserReducer;
sagas.js:
import { take, call, put, select, takeLatest, takeEvery } from 'redux-saga/effects';
import { push } from 'react-router-redux';
import { changeTabFromUrl, urlFromId } from 'containers/TabsPageChooser/actions';
import { makeSelectTab } from 'containers/TabsPageChooser/selectors';
import { CHANGE_TAB } from 'containers/TabsPageChooser/constants';
import { LOCATION_CHANGE } from 'react-router-redux';
function* doChangeTab(action) {
//Act as dispatch()
yield put(changeTabFromUrl(action.payload.pathname));
}
function* doChangeUrl(action) {
//Act as dispatch()
yield put(push(urlFromId(action.tab.tabId)));
}
// Individual exports for testing
export function* defaultSagas() {
yield takeEvery(LOCATION_CHANGE, doChangeTab);
yield takeEvery(CHANGE_TAB, doChangeUrl);
}
// All sagas to be loaded
export default [
defaultSagas,
];
我的问题尤其是最后一个文件LOCATION_CHANGE事件触发了changeTab操作,该操作又触发了CHANGE_TAB事件,触发了位置更改等...,
我做错了什么,我该怎么办?
答案 0 :(得分:0)
我终于成功了, 我改变了什么:
/*
*
* TabsChooser
*
*/
import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import { FormattedMessage } from 'react-intl';
import { createStructuredSelector } from 'reselect';
import { changeTab } from 'containers/App/actions';
import { makeSelectLocationState, makeSelectTabsChooser } from 'containers/App/selectors';
import messages from './messages';
import {Tabs, Tab} from 'material-ui/Tabs';
import FontIcon from 'material-ui/FontIcon';
const locationId = [
'/',
'/settings',
'/about',
];
export class TabsChooser extends React.Component { // eslint-disable-line react/prefer-stateless-function
render() {
this.contentsTab = [
{ route: this.props.onChangeTab.bind(null, locationId[0]), icon: <FontIcon className='material-icons'>home</FontIcon>, label: <FormattedMessage {...messages.home} />, },
{ route: this.props.onChangeTab.bind(null, locationId[1]), icon: <FontIcon className='material-icons'>settings</FontIcon>, label: <FormattedMessage {...messages.settings} />, },
{ route: this.props.onChangeTab.bind(null, locationId[2]), icon: <FontIcon className='material-icons'>favorite</FontIcon>, label: <FormattedMessage {...messages.about} />, },
];
let tabId = locationId.indexOf(this.props.tabLocation);
return (
<div>
<Tabs value={tabId} >
{this.contentsTab.map((tab, i) =>
<Tab key={i} value={i} icon={tab.icon} label={tab.label} onActive={tab.route} />
)}
</Tabs>
</div>
);
}
}
TabsChooser.propTypes = {
onChangeTab: React.PropTypes.func,
tabLocation: React.PropTypes.string,
};
function mapDispatchToProps(dispatch) {
return {
onChangeTab: (location) => dispatch(changeTab(location)),
};
}
const mapStateToProps = createStructuredSelector({
tabLocation: makeSelectTabsChooser(),
});
export default connect(mapStateToProps, mapDispatchToProps)(TabsChooser);
我现在发送位置而不是changeTab()中的标签ID, 我将action.js,reducer.js,selector.js和sagas.js移动到容器/ App
action.js:
/*
* App Actions
*
*/
import { CHANGE_TAB, TABCHANGE_LOCATION } from './constants'
export function changeTab(tabLocation) {
return {
type: CHANGE_TAB,
tabLocation,
};
}
export function changeLocation(tabLocation) {
return {
type: TABCHANGE_LOCATION,
tabLocation,
};
}
constants.js:
/*
* AppConstants
*/
export const CHANGE_TAB = 'app/App/CHANGE_TAB';
export const TABCHANGE_LOCATION = 'app/App/TABCHANGE_LOCATION';
reducer.js:
/*
* AppReducer
*
*/
import { fromJS } from 'immutable';
import {
CHANGE_TAB,
TABCHANGE_LOCATION,
} from './constants';
// The initial state of the App
const initialState = fromJS({
tabLocation: window.location.pathname // Initial location from uri
});
function appReducer(state = initialState, action) {
switch (action.type) {
case CHANGE_TAB:
return state.set('tabLocation', action.tabLocation);
case TABCHANGE_LOCATION:
return state.set('tabLocation', action.tabLocation);
default:
return state;
}
}
export default appReducer;
使用window.location.pathname设置initialState tabLocation,因此在app bootup中选择了右侧选项卡。
selector.js:
/**
* The global state selectors
*/
import { createSelector } from 'reselect';
const selectGlobal = (state) => state.get('global');
const makeSelectLocationState = () => {
let prevRoutingState;
let prevRoutingStateJS;
return (state) => {
const routingState = state.get('route'); // or state.route
if (!routingState.equals(prevRoutingState)) {
prevRoutingState = routingState;
prevRoutingStateJS = routingState.toJS();
}
return prevRoutingStateJS;
};
};
const makeSelectTabsChooser = () => createSelector(
selectGlobal,
(globalState) => globalState.getIn(['tabLocation'])
);
export {
selectGlobal,
makeSelectLocationState,
makeSelectTabsChooser,
};
sagas.js:
import { take, call, put, select, takeLatest, takeEvery, cancel } from 'redux-saga/effects';
import { push } from 'react-router-redux';
import { changeLocation } from './actions';
import { makeSelectTabsChooser } from './selectors';
import { CHANGE_TAB } from './constants';
import { LOCATION_CHANGE } from 'react-router-redux';
function* updateLocation(action) {
//put() act as dispatch()
const url = yield put(push(action.tabLocation));
}
function* updateTab(action) {
const loc = yield put(changeLocation(action.payload.pathname));
}
// Individual exports for testing
export function* defaultSagas() {
const watcher = yield takeLatest(CHANGE_TAB, updateLocation);
const watcher2 = yield takeLatest(LOCATION_CHANGE, updateTab);
}
// All sagas to be loaded
export default [
defaultSagas,
];
最后,传奇将它包起来。