Open xingbofeng opened 7 years ago
举一些简单的例子说说我写的不太周全的代码吧。
参考以下这段我已经修改后代码
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
action
store
或许你就会像我一样,少了一个判断吧?
其实不管是否去请求接口,dispatch之前and之后,若是同样的状态,做这个无谓的状态更新又有何意义呢?
我在action写了这样一个方法,其中server.enterHome()是一个经过封装的fetch方法,用于请求接口。
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是假的。
await
then
我们进行了如下修改:
export function home() { return async function (dispatch) { const resp = await server.enterHome() const { data } = await resp.json() dispatch(enterHome(data)); } }
以下是一段测试数据:
情况就是每一章的children对应的是每一节的内容,每一节有唯一的一个id进行标识。
id
我使用一个状态currentSection记录当前用户所在节的信息:
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, }, }, })
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()里写了这样一段丑陋的代码。
componentWillMount()
然而根本就不需要currentSection,毕竟它也是从同一个接口取出来的。只需要在render()里遍历一遍chapterInfos就好了啊……
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> ); }
希望以后写的代码越来越健壮吧。虽然我是真的不适合编程。哈哈哈哈。
举一些简单的例子说说我写的不太周全的代码吧。
为何你要去多次dispatch?
参考以下这段我已经修改后代码
这里的重点是通过
dispatch
一个action
,改变store
里面的状态。或许你就会像我一样,少了一个判断吧?
其实不管是否去请求接口,
dispatch
之前and之后,若是同样的状态,做这个无谓的状态更新又有何意义呢?你在await一个假Promise?
我在
action
写了这样一个方法,其中server.enterHome()
是一个经过封装的fetch
方法,用于请求接口。然后当时我们是这么调用的:
后来发现原来是
await
语句,会在第一次then
返回时就结束了,开始执行后面的同步代码。因而可以说这个await
是假的。我们进行了如下修改:
我曾一度想引入lodash进行对象判等?甚至还想用递归……
以下是一段测试数据:
情况就是每一章的children对应的是每一节的内容,每一节有唯一的一个
id
进行标识。我使用一个状态
currentSection
记录当前用户所在节的信息:然而每次作
dispatch
都要去对当前currentSection
和改变后的currentSection
进行判等。众所周知,对象判等相当消耗性能。所以探讨后是这样的解决办法:为何不把currentSection
用一个对象保存下来,使用唯一id
作为键值标识,不用对象判断,而判断唯一id
作为标识的对象是否存在呢?因此我改为下面这样:
其实都根本不用currentSection,遍历一次就好了
我甚至为了fix直接进入页面没数据的bug,在
componentWillMount()
里写了这样一段丑陋的代码。然而根本就不需要
currentSection
,毕竟它也是从同一个接口取出来的。只需要在render()
里遍历一遍chapterInfos
就好了啊……只能感叹,还需修行……
反思的时候我仿佛觉得自己写了一坨屎
这是我一个组件的代码:
乍一看啥也没错,但是
render()
里面啥也没有直接返回一个组件……那为何不直接把这个组件拉出来呢……这里做了多次遍历,却没有提取出来组件。还是感觉图样图森破啊……
因此后面是这么改的
希望以后写的代码越来越健壮吧。虽然我是真的不适合编程。哈哈哈哈。