曾经实现过Angular版,这次感觉用了高大上的React却写了更多的代码,需要的配置也更多了,有利有弊吧。
但这个“导航条问题”很有意思,涉及到在Redux中写timer,其实我很困惑,到底如何完美模拟用户的页面加载,
貌似浏览器并没有提供进度API,只能以这样拙劣的方式进行模拟,有兴趣的朋友不妨与我交流。
代码附上:
LoadingBar.jsx
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.jsx
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.js
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.js
export function show() { return { type: 'SHOW_LOADING' } } export function hide() { return { type: 'HIDE_LOADING' } }
loadingMiddleware.js
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()); } } }
配置Store
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, });