Reactjs React Redux router saga物料界面和当前路线对应的选项卡

Reactjs React Redux router saga物料界面和当前路线对应的选项卡,reactjs,redux,material-ui,redux-saga,Reactjs,Redux,Material Ui,Redux Saga,我对反应非常陌生,我正在使用+材质ui 我有这样的标签: 我希望能够更改当前选项卡,从而更改当前路线,反之亦然。 此外,当使用路由刷新页面时,它应该转到右侧选项卡 我的tabpagechooser容器组件如下: index.js: /* * * TabsPageChooser * */ import React, { PropTypes } from 'react'; import { connect } from 'react-redux'; import { FormattedM

我对反应非常陌生,我正在使用+材质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);
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;
/*
 * 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;
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,
];
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,
];
我的问题是,特别是最后一个文件,位置更改事件,触发了changeTab操作,而changeTab操作又触发了CHANGE TAB事件,从而触发了位置更改等等

我做错了什么,该怎么办?

我终于成功了, 我改变了什么:

/*
 *
 * 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);
constants.js:

/*
 *
 * TabsPageChooser constants
 *
 */

export const CHANGE_TAB = 'app/TabsPageChooser/CHANGE_TAB';

export const ROUTES_ID = [
  '/',
  '/settings',
  '/about',
];
/*
 * AppConstants
 */

export const CHANGE_TAB = 'app/App/CHANGE_TAB';
export const TABCHANGE_LOCATION = 'app/App/TABCHANGE_LOCATION';
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;
/*
 * 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;
initialState tabLocation是使用window.location.pathname设置的,因此在应用程序启动时选择右侧选项卡

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 } 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,
];
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,
];
最后,传奇故事结束了