xingbofeng.github.io
xingbofeng.github.io copied to clipboard
网易有道智能题库项目总结
举一些简单的例子说说我写的不太周全的代码吧。
为何你要去多次dispatch?
参考以下这段我已经修改后代码
async action({ store, params }) {
// 判断store里的id和当前id是否一致,若一致,则不请求后台
console.log('chapter')
const chapterInfos = store.getState().home.chapterInfos;
if (Object.keys(chapterInfos).length === 0 ||
chapterInfos.subject.id !== parseInt(params.chapter, 10)) {
await store.dispatch(chapter(params.chapter));
}
}
这里的重点是通过dispatch
一个action
,改变store
里面的状态。
或许你就会像我一样,少了一个判断吧?
其实不管是否去请求接口,dispatch
之前and之后,若是同样的状态,做这个无谓的状态更新又有何意义呢?
你在await一个假Promise?
我在action
写了这样一个方法,其中server.enterHome()
是一个经过封装的fetch
方法,用于请求接口。
export function home() {
return (dispatch) => {
server.enterHome()
.then(res => res.json())
.then(json => {
dispatch(enterHome(json.data));
})
.catch(err => {
console.log(err);
});
}
}
然后当时我们是这么调用的:
async action({ store }) {
if (Object.keys(store.getState().home.homeData).length === 0) {
await store.dispatch(home());
}
}
后来发现原来是await
语句,会在第一次then
返回时就结束了,开始执行后面的同步代码。因而可以说这个await
是假的。
我们进行了如下修改:
export function home() {
return async function (dispatch) {
const resp = await server.enterHome()
const { data } = await resp.json()
dispatch(enterHome(data));
}
}
我曾一度想引入lodash进行对象判等?甚至还想用递归……
以下是一段测试数据:
情况就是每一章的children对应的是每一节的内容,每一节有唯一的一个id
进行标识。
我使用一个状态currentSection
记录当前用户所在节的信息:
然而每次作dispatch
都要去对当前currentSection
和改变后的currentSection
进行判等。众所周知,对象判等相当消耗性能。所以探讨后是这样的解决办法:为何不把currentSection
用一个对象保存下来,使用唯一id
作为键值标识,不用对象判断,而判断唯一id
作为标识的对象是否存在呢?
因此我改为下面这样:
// action.js
// 当前节详情信息
export const CURRENT_SECTION = 'CURRENT_SECTION';
export const changeSection = makeActionCreator(CURRENT_SECTION, 'currentSection');
export function changeCurrentSection(data) {
return (dispatch, getState) => {
// 进行判断,如果store 里已经存了当前节信息那就不dispatch
const { home: { currentSection } } = getState();
if (!Object.keys(currentSection).includes(`${data.infosId}`)) {
dispatch(changeSection(data));
}
}
}
// reducer.js
case CURRENT_SECTION:
return Object.assign({}, state, {
currentSection: {
...state.currentSection,
[action.currentSection.infosId]: {
...action.currentSection,
},
},
})
其实都根本不用currentSection,遍历一次就好了
componentWillMount() {
const {
currentSection,
chapterInfos,
params,
} = this.props;
if (Object.keys(currentSection).length !== 0) {
this.setState({
currentSection: currentSection[params.section],
});
} else {
chapterInfos.subject.children.forEach(value => {
if (`${value.id}` === `${params.section}`) {
this.setState({
currentSection: {
chapterId: parseInt(params.chapter, 10),
children: value.children,
courseTitle: value.name,
},
});
}
});
}
}
我甚至为了fix直接进入页面没数据的bug,在componentWillMount()
里写了这样一段丑陋的代码。
然而根本就不需要currentSection
,毕竟它也是从同一个接口取出来的。只需要在render()
里遍历一遍chapterInfos
就好了啊……
let data = null;
chapterInfos.subject.children.forEach((secData) => {
if (secData.id === parseInt(params.section, 10)) {
data = secData;
}
});
只能感叹,还需修行……
反思的时候我仿佛觉得自己写了一坨屎
这是我一个组件的代码:
import React, { PropTypes } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import CourseCard from '../../components/p_home/CourseCard';
import * as actions from '../../actions/home';
class HomePage extends React.Component {
static propTypes = {
homeData: PropTypes.object.isRequired,
changeCurrentSection: PropTypes.func.isRequired,
};
componentDidMount() {
if (window.ydk !== undefined) window.ydk.hideLoading() // 隐藏加载中按钮
}
render() {
const { homeData, changeCurrentSection } = this.props;
if (Object.keys(homeData).length === 0) {
return null
}
return (
<CourseCard
homeData={homeData}
changeCurrentSection={changeCurrentSection}
/>);
}
}
function mapStateToProps(state) {
return {
homeData: state.home.homeData,
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(actions, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(HomePage);
乍一看啥也没错,但是render()
里面啥也没有直接返回一个组件……那为何不直接把这个组件拉出来呢……
import React, { PropTypes } from 'react';
import s from './style.css';
import Link from '../../Common/Link';
class CourseCard extends React.Component {
static propTypes = {
homeData: PropTypes.object.isRequired,
changeCurrentSection: PropTypes.func.isRequired,
};
render() {
// latest是最近做过的题目,subjects是章节内容
const { homeData, changeCurrentSection } = this.props;
const subjects = homeData.subjects || [];
const latest = homeData.latest || [];
return (
<div className={s.container}>
<header>智能题库</header>
<main>
{latest.length === 0 ?
null :
<div className={s.itemsBox}>
<h1>—— 最近练习 ——</h1>
{latest.map((value, index) =>
<div className={s.cardsItem} key={index}>
<Link
to={`/home/${value.chapterId}/${value.subject.id}`}
onClick={() => changeCurrentSection({
children: value.subject.children,
chapterId: value.chapterId,
courseTitle: value.subject.name,
})}
>
{value.subject.name}
</Link>
</div>)}
</div>
}
{/* 这里要做两次循环,一次遍历每类考试名,遍历后的结果再遍历每门课 */}
{subjects.length === 0 ?
null :
subjects.map((value, index) =>
<div className={s.itemsBox} key={index}>
<h1>{value.category}</h1>
{value.subjects.map((v, i) =>
<div className={s.cardsItem} key={i}>
<Link
to={`/home/${v.id}`}
>
{v.name}
</Link>
</div>,
)}
</div>,
)
}
</main>
</div>
);
}
}
这里做了多次遍历,却没有提取出来组件。还是感觉图样图森破啊……
因此后面是这么改的
render() {
const { homeData } = this.props;
const subjects = homeData.subjects || [];
const latest = homeData.latest || [];
return (
<div className={s.container}>
{latest.length === 0 ? null :
<div className={s.latestItemsBox}>
<div className={s.title}>最近练习</div>
<div className={s.itemCont}>
{latest.map((value, index) =>
<LatestExercise
value={value}
key={`latest_card_${index}`}
linkClick={this.linkClick}
/>)}
</div>
</div>}
{subjects.length === 0 ? null :
subjects.map((value, index) =>
<CourseCard
key={`course_card_${index}`}
value={value}
linkClick={this.linkClick}
/>,
)}
</div>
);
}
希望以后写的代码越来越健壮吧。虽然我是真的不适合编程。哈哈哈哈。