react开发辅助工具
chrome扩展包
1、React Devtools:检视React组件树形结构:https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi
2、Redux Devtools:检视Redux数据流:https://chrome.google. com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibtkpmmfibljd
3、React Perf:发现React组件渲染的性能问题:https://google.com/webstore/detail/react-perf/hacmcodfllhbnekmghgdlplbdnahrnhmm
如果访问谷歌浏览商店哟困难,可以访问http://chrome-extension-downloader.com/,在输入框输入上面连接最后面的字符串,点击下载扩展包按钮即可
1、组件生命周期:
组件生命周期分为三个阶段:
创建过程(挂载)
更新过程
卸载过程
了解:每个阶段执行时机;每个阶段钩子函数执行顺序
创建过程
执行时机:页面加载时(组件创建时)
钩子函数执行顺序也按照书写顺序
constructor:触发时机-创建组件时最先执行;用于初始化state、为事件处理程序绑定this
getInitialState
getDefaultProps
componentWillMount,这里不用,就算在这里修改数据,但是渲染的时候也不会更新,初始化数据最好在constructor;
render:触发时机---每次组件渲染都会触发;用于渲染UI(不能调用 setState(),因为state改变之后,会触发render)
componentDidMount:触发时机---组件挂载后(完成DOM渲染);用于发送网络请求、DOM操作
1)constructor并不是类中必须定义的
需要用到state的时候才定义,用于初始化state
绑定成员函数的this环境
用到props的时候,不定义constructor,初始化super(props),能继承父的props吗????
1)getInitialState\\getDefaultProps在createClass中才调用
2)componentWillMount紧跟render前面执行,但是componentDidMount在整个页面的组件render完之后,在按照render顺序执行相应的componentDidMount
3)componentWillMount、componentDidMount区别
componentWillMount可以在浏览器和服务端调用;componentDidMountz只能在浏览器调用,因为装载是创建组件并放在DOM上,而服务端并不会产生DOM树,所以只能在浏览器上调用
4)render是React.component必须实现的方法
render不做世纪渲染动作,返回JSX描述结构,React操作渲染过程
render函数返回null false,则这个组件不需要渲染任何元素
render函数应该是一个纯函数
更新过程
执行时机:setState()调用 forceUpdate() 组件接收到新的props
执行顺序按照下面的顺序:
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
render:触发时机-----每次组件渲染都会触发;渲染UI
componentDidUpdate:触发时机-----组件更新后(完成DOM渲染);用于发送网络请求、DOM操作,如果要setState,要放在一个if里面,否则会导致递归更新
注意:并不是所有更新都会执行上面的方法
1)componentWillReceiveProps(nextProps)
触发时机:只要父组件的render调用,不管父组件传给子组件的props是否变化,都会触发组件的componentWillReceiveProps
通过setState触发的更新,不会调发该方法
卸载
执行时机:组件从页面消失
钩子函数:componentWillUnmount;
触发时机:组件卸载;
作用:执行清理工作(清定时器等)
2、数据:props (外部传给组件,不允许修改) state(内部数据,动态修改状态)
1、props
1)可以通过propTypes设置和检查props的类型
className.propTypes = {
caption: PropTypes.string.isRequired,
initValue: PropTypes.number
}
这个类型检查只是在控制台报错,不会影响交互,所以,应该只在开发环境使用,生产就应该去掉,babel-react-optimize就可以,保证在生产上才使用
2)className.defaultProps可以给props设置默认值
当调用组件的时候,没有定义给属性,则会使用默认值
3、state
通过className.defaultProps可以给props设置默认值,在用props初始化state的时候就不需要写默认值了
关于react中数据通讯的三种方式
父组件传递数据给子组件、子组件传递数据给父组件、相邻组件传递数据、多层嵌套的的组件间传递数据
1)、父传给子组件数据
1)通过props传递数据给子组件,子组件引用上定义props,如name,值为父组件中在state上定义的变量为name
2)子函数组件通过props拿数据props.name,类组件通过this.props.name拿数据
2)、子组件传递数据给父组件
1)在父组件中定义方法handleData
2)在子组件引用上添加props,如onClick,值为handleData
3)在子组件中定义触发事件,调用函数组件调用props.handleData(),类组件调用this.props.handleData()
3)、相邻组件传递数据
1)通过在父组件的state中定义共享变量a
2)父组件中定义改变共享变量值的方法changeA()
3)将变量a和方法changeA作为props给子组件
从而达到相邻组件传递数据的目的
4)、深度嵌套组件传递数据
如A=》B=》C=》D,现在需要将数据从A传到D,除了props形式,还可以通过context形式传递
1) 组件Provider提高数据,组件Consumer 消费数据
const { Provider, Consumer } = React.createContext()
2) 使用Provider组件作为父节点,设置value属性表示要传递的数据
<Provider value="pinker">
....
</Provider>
3) 在要接收数据的子组件里面调用Consumer组件接收数据
<Consumer>
{data => <span>接收到的数据为:{data}</span>}
</Consumer>
4、组件复用
复用:复用state,操作state的方法(组件状态逻辑)
组件复用两种方式:
1)组件为入参,返回为组件----高阶组件HOC
2)render props模式
render props模式
组件创建时,组件内部的的render方法使用this.props.render,即
render() {
return this.props.render(this.state)
}
使用组件时,添加一个值为函数的props,通过函数参数来获取组件内部数据,根据组件在实际编写ui
<Mouse render={(mouse) => (
<p>数据为:{mouse.x}、{mouse.y}</p>
)}>
注意,可以使用props.children代替props.render属性,即在使用组件的时候可以不再标签里面定义属性render
组件创建时候,将render函数改成下面形式
render() {
return this.props.children(this.state)
}
组件使用时:
<Mouse>
{
(mouse => {
return (
<P>数据为:{mouse.x}、{mouse.y}</P>
)
})
}
</Mouse>
高阶组件
HOC是一个函数,参数为组件,返回新的组件
displayName属性、传递props避免props丢失
给原组件添加新的props属性
class Mouse extends React.component{
render () {
return <WrappedComponent {...this.state} />
}
}
使用步骤:
1)创建以with开头的函数
2)指定函数参数,参数以大写字母开头
3)在函数内部创建类组件,提供复用的状态逻辑代码
4)在该组件内,渲染参数组件,同时将状态通过props传递给参数组件
5)调用高阶组件,传入要增强的组件,通过返回值拿到增强的组件,并渲染到页面
// 创建高阶组件
function withMouse(WrappedComponent) {
// 该组件提供服用的逻辑
class Mouse extends ReadableByteStreamController.component {
// 鼠标状态
state = {
x: 0,
y: 0
}
handleMouseMove = (e) => {
this.setState({
x: e.clientX,
y: e.clientY
})
}
// 控制鼠标状态逻辑
componentDidMount() {
window.addEventListener('mousemove', this.handleMouseMove)
}
// 解绑事件
componentWillUnmount() {
window.removeEventListener('mousemove', this.handleMouseMove)
}
// 渲染
render() {
return (
// 通过props,将数据传递给传入组件
<WrappedComponent {...this.state}></WrappedComponent>
)
}
}
return Mouse
}
// 测试高阶组件
const Position = (props) => (
<p>鼠标当前位置:(x: {props.x}, y: {props.y})</p>
)
const Cat = (props) => (
<img style={
{
position: 'absolute',
top: props.y - 64,
left: props.x - 64
}} alt="cat" />
)
// 获取增强后的组件
const MousePosition = withMouse(Position)
// 创建高阶组件
function withMouse(WrappedComponent) {
// 该组件提供服用的逻辑
class Mouse extends ReadableByteStreamController.component {
// 鼠标状态
state = {
x: 0,
y: 0
}
handleMouseMove = (e) => {
this.setState({
x: e.clientX,
y: e.clientY
})
}
// 控制鼠标状态逻辑
componentDidMount() {
window.addEventListener('mousemove', this.handleMouseMove)
}
// 解绑事件
componentWillUnmount() {
window.removeEventListener('mousemove', this.handleMouseMove)
}
// 渲染
render() {
return (
// 通过props,将数据传递给传入组件
<WrappedComponent {...this.state}></WrappedComponent>
)
}
}
return Mouse
}
// 测试高阶组件
const Position = (props) => (
<p>鼠标当前位置:(x: {props.x}, y: {props.y})</p>
)
const Cat = (props) => (
<img style={
{
position: 'absolute',
top: props.y - 64,
left: props.x - 64
}} alt="cat" />
)
// 获取增强后的组件
const MousePosition = withMouse(Position)
const CatPosition = withMouse(Cat)
// 在页面中渲染MousePosition\CatPosition组件
问题:以上两个组件渲染出来的名称都是<Mouse>
原因:默认情况下,React使用组件名称作为displayName
解决方法:为高阶组件设置displayname,便于调试区分不同组件
设置方式:
// 设置displatname
Mouse.displayName = `With${getDisplayName(WrappedComponent)}`
function getDisplayName(WrappedComponent) {
// 有设置属性displayName,WrappedComponent.name表示创建的新高阶组件名称
return WrappedComponent.displayName || WrappedComponent.name || 'Component'
}
// 创建高阶组件
function withMouse(WrappedComponent) {
// 该组件提供服用的逻辑
class Mouse extends ReadableByteStreamController.component {
// 鼠标状态
state = {
x: 0,
y: 0
}
handleMouseMove = (e) => {
this.setState({
x: e.clientX,
y: e.clientY
})
}
// 控制鼠标状态逻辑
componentDidMount() {
window.addEventListener('mousemove', this.handleMouseMove)
}
// 解绑事件
componentWillUnmount() {
window.removeEventListener('mousemove', this.handleMouseMove)
}
// 渲染
render() {
return (
// 通过props,将数据传递给传入组件
<WrappedComponent {...this.state}></WrappedComponent>
)
}
}
// 设置displatname
Mouse.displayName = `With${getDisplayName(WrappedComponent)}`
return Mouse
}
function getDisplayName(WrappedComponent) {
// 有设置属性displayName,WrappedComponent.name表示创建的新高阶组件名称
return WrappedComponent.displayName || WrappedComponent.name || 'Component'
}
// 测试高阶组件
const Position = (props) => (
<p>鼠标当前位置:(x: {props.x}, y: {props.y})</p>
)
const Cat = (props) => (
<img style={
{
position: 'absolute',
top: props.y - 64,
left: props.x - 64
}} alt="cat" />
)
// 获取增强后的组件
const MousePosition = withMouse(Position)
const CatPosition = withMouse(Cat)
// 在页面中渲染MousePosition组件
问题:传递props,避免props丢失
<MousePosition a={1} />
// 在Mouse中的render,state和props一起传递
<WrappedComponent {...this.state} {...this.props}></WrappedComponent>