2017-01-26 9 views
0

Я довольно новыми реагировать, и я использую react-boilerplate + материал-щРеагировать Redux маршрутизатор сага материал-интерфейса и вкладки, соответствующие текущему маршруту

У меня есть вкладки, как так: Three tabs

И Я хочу иметь возможность изменить текущую вкладку, чтобы она изменила текущий маршрут и наоборот. Также при обновлении страницы с помощью маршрута она должна перейти на правую вкладку.

Так у меня есть 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

я, наконец, удалось, Что изменилось:

/* 
* 
* 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(), я двигаюсь action.js, reducer.js, селектор. 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; 

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, 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, 
]; 

Итак, наконец саги завернуть.

 Смежные вопросы

  • Нет связанных вопросов^_^