React 最佳实践一、 React 与 AJAX
React 只负责处理 View 这一层,它本身不涉及网络请求 /AJAX:
第一,用什么技术从服务端获取数据;
第二,获取到的数据应该放在 react 组件的什么位置。
事实上是有很多的:fetch()、fetch polyfill、axios...
其中最需要我们关注的是window.fetch(),它是一个简洁、标准化的 javascript 的 Ajax API 。
在 Chrome 和 Firefox 中已经可以使用,如果需要兼容其他浏览器,可以使用 fetch polyfill 。
ajax实践:
1.所有的数据请求和管理都存放在唯一的一个根组件让父组件 /根组件集中发送所有的 ajax 请求,
把从服务端获取的数据统一存放在这个组件的 state 中,再通过 props 把数据传给子组件。
这种方法主要是针对组件树不是很复杂的小型应用。缺点就是当组件树的层级变多了以后,
需要把数据一层一层地传给子组件,写起来麻烦,性能也不好。2.设置多个容器组件专门处理数据请求和管理其实跟第一种方法类似,只不过设置多个容器组件来负责数据请求和状态管理。
这里我们需要区分两种不同类型的组件,
一种是展示性组件( presentational component ),
另一种是容器性组件( container component )。
展示性组件本身不拥有任何状态,所有的数据都从容器组件中获得,在容器组件中发送 ajax 请求。3.使用 Redux 或 Relay 的情况Redux 管理状态和数据, Ajax 从服务器端获取数据,所以很显然当我们使用了 Redux 时,
应该把所有的网络请求都交给 redux 来解决。具体来说,应该是放在Async Actions。
如果用其他类 Flux 库的话,解决方式都差不多,都是在 actions 中发送网络请求。
把计算和条件判断都交给render()方法吧
echarts
概览页面 -》 echats图修改的时候犯了个错 yield put({ type: ' DescribeInvadeTrend' }); --》
发现没有请求 咋回事!!!! 注意观察代码...
React setState
setState(updater, callback)这个方法是用来告诉react组件数据有更新,有可能需要重新渲染。它是异步的,react通常会集齐一批需要更新的组件,然后一次性更新来保证渲染的性能,所以这就给我们埋了一个坑:
那就是在使用setState改变状态之后,立刻通过this.state去拿最新的状态往往是拿不到的。
所以第一个使用要点就是:如果你需要基于最新的state做业务的话,可以在componentDidUpdate或者setState的回调函数里获取。(注:官方推荐第一种做法)
设想有一个需求,需要在在onClick里累加两次,如下:
onClick = () => {this.setState({ index: this.state.index + 1 });this.setState({ index: this.state.index + 1 });}
在react眼中,这个方法最终会变成
Object.assign(previousState,{index: state.index+ 1},{index: state.index+ 1},...
)
由于后面的数据会覆盖前面的更改,所以最终只加了一次.所以如果是下一个state依赖前一个state的话,推荐给setState传function
onClick = () => {this.setState((prevState, props) => {return {quantity: prevState.quantity + 1};});this.setState((prevState, props) => {return {quantity: prevState.quantity + 1};});
}
以上是使用setState的两个注意事项,接下来我们来看看setState被调用之后,更新组件的过程:
ReactBaseClassses.js
ReactComponent.prototype.setState = function (partialState, callback) {// 将setState事务放进队列中this.updater.enqueueSetState(this, partialState);if (callback) {this.updater.enqueueCallback(this, callback, 'setState');}
};
这里的partialState可以传object,也可以传function,它会产生新的state以一种Object.assgine()的方式跟旧的state进行合并。
二、enqueueSetState
enqueueSetState: function (publicInstance, partialState) {// 获取当前组件的instancevar internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState');// 将要更新的state放入一个数组里var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);queue.push(partialState);// 将要更新的component instance也放在一个队列里enqueueUpdate(internalInstance);}
.
..消化...
state和props
React 的核心思想是组件化的思想,而React 组件的定义可以通过下面的公式描述:
UI = Component(props, state)
不是对props 和state 基本用法的介绍,而是尝试从更深层次解释props 和 state,并且归纳使用它们时的注意事项。
一句话概括,props 是组件对外的接口,state 是组件对内的接口。
组件的props 和 state都和组件最终渲染出的UI直接相关。
组件中用到的一个变量是不是应该作为组件state,可以通过下面的4条依据进行判断:
- 这个变量是否是通过props从父组件中获取?如果是,那么它不是一个状态。
- 这个变量是否在组件的整个生命周期中都保持不变?如果是,那么它不是一个状态。
- 这个变量是否可以通过state 或props 中的已有数据计算得到?如果是,那么它不是一个状态。
- 这个变量是否在组件的render方法中使用?如果不是,那么它不是一个状态。这种情况下,这个变量更适合定义为组件的一个普通属性(除了props 和 state以外的组件属性 ),例如组件中用到的定时器,就应该直接定义为this.timer,而不是this.state.timer。
不能依赖当前的props计算下个state,因为props的更新也是异步的。
Object.assign(previousState,{quantity: this.state.quantity + 1},{quantity: this.state.quantity + 1}
)
当点击一次购买按钮,购买的数量就会加1,如果我们连续点击了两次按钮,就会连续调用两次this.setState({quantity: this.state.quantity + 1})
// 正确
this.setState((preState, props) => ({counter: preState.quantity + 1;
}))
State 的更新是一个浅合并(Shallow Merge)的过程。
例如,一个组件的state为:
this.state = {title : 'React',content : 'React is an wonderful JS library!'
}
this.setState({title: 'Reactjs'});
React会合并新的title到原来的组件state中,同时保留原有的状态content,合并后的state为:
{title : 'Reactjs',content : 'React is an wonderful JS library!'
}
State与Immutable
React官方建议把state当作不可变对象,一方面是如果直接修改this.state,组件并不会重新render;另一方面state中包含的所有状态都应该是不可变对象。
如有一个数组类型的状态books,当向books中增加一本书时,使用数组的concat方法或ES6的数组扩展语法(spread syntax):
// 方法一:使用preState、concat创建新数组
this.setState(preState => ({books: preState.books.concat(['React Guide']);
}))// 方法二:ES6 spread syntax
this.setState(preState => ({books: [...preState.books, 'React Guide'];
}))
当从books中截取部分元素作为新状态时,使用数组的slice方法:
// 使用preState、slice创建新数组
this.setState(preState => ({books: preState.books.slice(1,3);
}))
当从books中过滤部分元素后,作为新状态时,使用数组的filter方法:
this.setState(preState => ({books: preState.books.filter(item => {return item != 'React'; });
}))
注意不要使用push、pop、shift、unshift、splice等方法修改数组类型的状态,因为这些方法都是在原数组的基础上修改,而concat、slice、filter会返回一个新的数组。
状态的类型是简单对象(Plain Object)
this.state = {owner = {name: '老干部',age: 30}
}
this.setState(preState => ({owner: Object.assign({}, preState.owner, {name: 'Jason'});
}))
//第一种方法
this.setState(preState => ({owner: {...preState.owner, name: 'Jason'};
}))
//第二种方法
总结一下,创建新的状态的关键是,避免使用会直接修改原对象的方法,而是使用可以返回一个新对象的方法。当然,也可以使用一些Immutable的JS库,如Immutable.js,实现类似的效果。
那么,为什么React推荐组件的状态是不可变对象呢?一方面是因为不可变对象方便管理和调试,了解更多可参考这里: http://redux.js.org/docs/faq/ImmutableData.html#benefits-of-immutability
另一方面是出于性能考虑,当组件状态都是不可变对象时,我们在组件的shouldComponentUpdate方法中,仅需要比较状态的引用就可以判断状态是否真的改变,从而避免不必要的render方法的调用。当我们使用React 提供的PureComponent时,更是要保证组件状态是不可变对象,否则在组件的shouldComponentUpdate方法中,状态比较就可能出现错误。
porps的改变为什么是异步的? ????????
React顶级API
React.Component
class Greeting extends React.Component {render() {return <h1>Hello, {this.props.name}</h1>}
}
ReactDOM.render(<Greeting name={"zhangyatao"}/>,document.getElementById('root)
)
React.PureComponet
它实现了shouldComponentUpdate()对props和state的进行浅比较。
React.PureComponent的shouldComponentUpdate()仅会对对象进行浅比较,如果对象包含复杂的数据结构,对于深层次的差异有可能会产生false-negatives(假阴性,相当于医院中的错诊)。
React.Children
React.children提供了处理this.props.children中那些不透明的数据结构的一些工具函数。
React.Children.map
React.Children.map(children, function[(thisArg))
React.Children.forEach(children, function[(thisArg)])
和React.Children.map相同,只不过不会返回一个数组。
React.Children.count
React.Children.count(children)
返回children中的组件总数。
React.Children.toArray
React.Children.toArray(children)
将子元素中的不透明数据结构作为一个一维数组返回。如果你想在render方法中操作children集合,特别是如果你想在传递它之前重新排序或切割this.props.children,这个方法将非常有用。
React.PropTypes
React.PropTypes是一系列类型验证器的集合,可以与组件的propTypes对象一起使用,以验证传递到组件的props。
深入理解JSX
从根本上讲,JSX就是提供了一个React.createElement(component, props, ...children)函数的语法糖。就像下面的JSX代码:
<MyButton color="blue" shadow={2}>Click Me
</MyButton>
经过编译后为:
React.createElement(MyButton,{color: 'blue', shadow: 2},'Click Me'
)
<div className="sidebar" />
经过编译后为:
React.createElement('div',{className: 'sidebar'},null
)
如果你想测试一些特定的JSX是如何转换成JavaScript的话,你可以试试在线Babel编译器。
由于JSX编译的本质是对React.createElement的调用,因此React库也必须始终在JSX代码的作用域中。
还可以使用JSX中的点表示符来引用React组件
const MyComponents = {DatePicker(props) {return <div>这里有一个颜色为{props.color}的日期选择器</div>}
};function BlueDataPicker(props) {return <MyComponents.DatePicker color="blue" />
}
字符串直接量
<MyComponent message="hi " /><MyComponent message={'hi'} />
<MyComponent message='<3' /><MyComponent message={'<3'} />
Props传递
如果你有一个对象类似的数据作为props,并且想在JSX中传递它,你可以使用...作为一个“spread”运算符传递整个props对象。 这两个组件是等效的:
function App() {return <Greeting firstName="yatao" lastName="zhang" />;
}function App() {const props = {firstName: 'yatao', lastName: 'zhang'};return <Greeting {...props} />;
}
JSX中的子元素和子组件
function MyComponent(props) {return <div>{props.children}<div>; //=> <div>hello</div>
}
JSX会删除行的开始和结尾处的空格。 它也会删除中间的空行。 与标签相邻的空行被会被删除;
在字符串文本中间出现的空行会缩合成一个空格。 所以这些都渲染相同的事情:布尔值、null、undefined在渲染时会被自动忽略
<div></div><div>{false}</div><div>{null}</div><div>{true}</div>
如果你想要一个值如false,true,null或undefined出现在输出中,你必须先将它转换为字符串:
react性能查看工具
在最新的React16版本中,我们可以直接在url后加上?react_pref,就可以在chrome浏览器的performance,我们可以查看User Timeing来查看组件的加载时间。

ps: 关于key的使用
关于key的使用我们要注意的是,这个key值要稳定不变的,就如同身份证号之于我们是稳定不变的一样。
一个常见的错误就是,拿数组的的下标值去当做key,这个是很危险的,代码如下,我们一定要避免。
将key设置为一个特殊字段,保证其唯一性
其他的检测工具
react-perf-tool 为React应用提供了一种可视化的性能检测方案,该工程同样是基于React.addons,但是使用图表来显示结果,更加方便。