写了一个基于React+Redux的仿Github进度条

时间:2023-03-13 16:23:44

曾经实现过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,
});