曾經實現過Angular版,此次感受用了高大上的React卻寫了更多的代碼,須要的配置也更多了,有利有弊吧。react
但這個「導航條問題」頗有意思,涉及到在Redux中寫timer,其實我很困惑,到底如何完美模擬用戶的頁面加載,redux
貌似瀏覽器並無提供進度API,只能以這樣拙劣的方式進行模擬,有興趣的朋友不妨與我交流。瀏覽器
代碼附上:app
LoadingBar.jsxide
import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; export class LoadingBar extends Component { constructor(props) { super(props); this.state = { timer: null, style: { display: 'none', position: 'absolute', width: '0%', height: '3px', backgroundColor: 'blue', transition: 'width 400ms ease-out, height 400ms linear' } }; } componentWillReceiveProps(nextProps) { if (nextProps.status) { let progress = 0; this.setState({ style: Object.assign({}, this.state.style, { width: '0%' }) }); let timer = setInterval(() => { if (progress <= (100 - nextProps.step)) { this.setState({ style: Object.assign({}, this.state.style, { width: `${progress += nextProps.step}%`, display: 'block' }) }); } }, nextProps.speed); this.setState({ timer: timer }); } else { clearInterval(this.state.timer); this.setState({ timer: null, style: Object.assign({}, this.state.style, { width: '100%', display: 'none' }) }); } } render() { return ( <div> <div style={this.state.style} className={this.props.className}></div> <div style={{ display: 'table', clear: 'both' }}></div> </div> ) } } LoadingBar.propTypes = { className: PropTypes.string, speed: PropTypes.number, step: PropTypes.number, status: PropTypes.bool, } function mapStateToProps(state) { return {...state.loading}; } export default connect(mapStateToProps)(LoadingBar)
App.jsxthis
import LoadingBar from 'LoadingBar'; const App = ({children}) => { return ( <div> <LoadingBar speed={5} step={2} /> {children} </div> ); }; App.propTypes = { children: PropTypes.object }; export default App;
loadingReducer.jsspa
export default function loading( state = { status: false }, action = {} ) { switch (action.type) { case 'SHOW_LOADING': return Object.assign({}, state, { status: true, }); case 'HIDE_LOADING': return Object.assign({}, state, { status: false, }); default: return state } }
loadingActions.jscode
export function show() { return { type: 'SHOW_LOADING' } } export function hide() { return { type: 'HIDE_LOADING' } }
loadingMiddleware.jscomponent
import { show, hide } from './loadingActions'; const defaultTypeSuffixes = ['REQUEST', 'SUCCESS', 'FAILURE'] export default function loadingBarMiddleware(config = {}) { const typeSuffixes = config.typeSuffixes || defaultTypeSuffixes; return ({ dispatch }) => next => action => { next(action); if (action.type === undefined) { return; } const [PENDING, FULFILLED, REJECTED] = typeSuffixes; const isPending = `_${PENDING}`; const isFulfilled = `_${FULFILLED}`; const isRejected = `_${REJECTED}`; if (action.type.indexOf(isPending) !== -1) { dispatch(show()); } else if (action.type.indexOf(isFulfilled) !== -1 || action.type.indexOf(isRejected) !== -1) { dispatch(hide()); } } }
配置Storeblog
import { createStore, applyMiddleware } from 'redux'; import { loadingMiddleware } from 'loadingMiddleware'; import rootReducer from './reducers'; const store = createStore( rootReducer, applyMiddleware(loadingMiddleware()) )
配置RootReducer
import { combineReducers } from 'redux'; import { loadingReducer } from './loadingReducer'; const reducer = combineReducers({ loading: loadingReducer, });