园子都荒废两个月了,实在是懒呀..html
近段时间用React开发了几个页面,在使用过程当中着实碰到了一些问题,估计刚开始学习的伙伴们都会遇到各类各样的坑react
总结记录一下,只看文档是碰不上问题的,内容基础也不基础,高手还请绕道哈哈哈npm
文章略长,整个目录吧,想看哪儿看哪儿bootstrap
首先,须要核心库react.js与React的DOM操做组件react-dom.jsreact-native
其次,若是须要在当前HTML页面中直接写react的代码,就要引入browser.js文件,用于解析相关的JSX语法,同时,script标签指定好type数组
引入browser是为了在浏览器端能直接解析JSX,不过至关耗时,因此建议在本地解析以后再引入ES5的语法文件。固然,JSX语法是没必要要的,只是推荐使用。浏览器
经过ReaactDOM.render方法渲染,参数1指定组件,参数2指定标签元素babel
使用babel工具对文件进行解析,Sublime Text中怎么配置babel编译?dom
查看编译后的文件异步
能够看到,JSX语法,核心就是React的createElement方法,我能够也直接使用这个方法建立。
这一丁点代码就编译了那么久,确实应该在本地先编译好
除了直接在浏览器引入react和react-dom以外,既然须要本地先编译,也可使用构建工具如Webpack,不只支持ES6与JSX的解析,还提供了一系列如代码压缩文件合并的功能,也便于管理,没必要每次都得手动编译
能够经过npm工具安装react和react-dom包后,引入直接使用(须要ES6基础)
这里就不展开说明了,有兴趣的能够自行去查查相关用法
JSX是React中和重要的部分,直观的表现是将HTML嵌入到了JS中,经过工具(如Babel)编译成支持的JS文件
var Info = React.createClass({ render: function() { return <p className="user">{this.props.name}</p> } }); ReactDOM.render( <Info name="Jack" />, document.getElementById('box') );
能够看到,return关键字后接上一个<p>标签,其中使用{}置入了JS语法。
var Info = React.createClass({ render: function() { return <p className="user"> { this.props.name == 'Jack' ? <span>is Jack</span> : '' } </p> } });
须要注意的是,三目运算符以后也只能接一个父级的标签,不然会报错
还能够置入组件
var Jack = React.createClass({ render: function() { return <p>I'm Jack</p> } }); var Pual = React.createClass({ render: function() { return <p>I'm Pual</p> } }); var Info = React.createClass({ render: function() { return ( <div className="user"> { this.props.name == 'Jack' ? <Jack /> : <Pual /> } </div> ) } }); ReactDOM.render( <Info name="Pual" />, document.getElementById('box') );
在HTML标签中使用非原始HTML支持的属性(可加前缀data-),会被React忽略,class关键字须要换成className等
事件绑定须要使用camelCase形式(如onClick)
var Info = React.createClass({ render: function() { return <p className="user" me="me" name="myName">{this.props.name}</p> } });
JSX支持ES6中不少语法,包括...这个东西。有时不想一个个属性写,为了代码美观,可使用
var Info = React.createClass({ render: function() { var myAttr = { 'title': 'myTitle', 'age': 10, 'data-age': 10, 'onClick': function() { console.log('onClick'); }, 'onclick': function() { console.log('onclick'); } } return <p className="user" me="me" {...myAttr}>{this.props.name}</p> } }); ReactDOM.render( <Info name="Jack" />, document.getElementById('box') );
编译后将自动展开,其中age被忽略,data-age被保留,onclick被忽略,onClick被保留
因为React对事件的绑定处理忽略了原始支持的onclick属性,在使用其余JS库时,可能会遇到问题
如WdatePicker日期插件,它的使用方式是直接在HTML中绑定
<input type="text" name="" onclick="WdatePicker()" />
<input type="text" name="" onClick="WdatePicker()" />
但转到React中就不适用了,onclick会直接被忽略,onClick由于传的不是函数也被忽略,因此须要换个法子
render() { // return <input type="text" name="" onclick="WdatePicker()" /> // return <input type="text" name="" onClick="WdatePicker()" /> let clickEvent = { onClick: function(e) { console.log(e); WdatePicker(e); } }; return <input type="text" name="date" ref="date" {...clickEvent} /> }
这样一来就能绑定上事件,此日期插件须要一个event对象,然而点击后报错了,调试输出该对象彷佛有些奇特
再换种方式,在组件渲染以后直接绑定,成功
componentDidMount() { let date = ReactDOM.findDOMNode(this.refs.date); date.onclick = function(e) { console.log(e); WdatePicker(e); } }
虽然说这是插件使用方式的不合理,但React传过来的event对象也已经不是原始的event对象了
举个例子,对于<input>标签
<input type="text" >
通常的HTML中这样是支持的,但在JSX中会报错
须要加个斜杠,同理用于<img>等标签
<input type="text" />
React中有属性与状态之分,都是为了方便存储或管理数据
一旦定义,就再也不改变的数据
通常来讲,会经过在HTML标签中添加属性的方式,让子组件获取到该props
ReactDOM.render( <Info name="Jack" />, document.getElementById('box') );
则Info组件中就能够经过this.props.name获取到该属性
也能够在组件中本身定义初始的属性,若是父有传name属性,则该初始属性被覆盖
getDefaultProps: function() { return { name: 'defaultName' }; }
还能够定义属性的类型,是否必须
propTypes: { name: React.PropTypes.string.isRequired }
这里定义了name属性必须有且为字符串,假设传入的是number类型(注意使用{}包裹,不然始终是字符串),则有警告
ReactDOM.render( <Info name={10} />, document.getElementById('box') );
虽然有修改props的方法,但不建议对props进行修改,若是要修改,就使用state吧
状态是React中定义以后可改变的数据,只能在组件内部定义
getInitialState: function() { return { age: 10 }; }
在须要修改状态的时候,调用this.setState()方法便可(注意不能直接设置this.state = newObj)
this.setState({ age: this.state.age + 1 });
注意必须初始化state对象,即初始化时至少要返回一个空的state对象,age属性的初始化是没必要要的,只是为了便于管理
React的setState方法是异步的,在其中取state.age可能取不到预期的值(不过目前还没遇到过)
这里的异步包含了两个概念
2.1 调用的时机异步
React的组件有生命周期,在componentWillUpdate与render这两个时期之间才会调用
2.2 调用以后的异步
setState其实是一个异步方法,可带两个参数
this.setState({ age: this.state.age + 1 }, function() { });
更好的作法是直接在第一个参数使用函数,如此便保证了函数内部能取到正确的值,在大型复杂的组件中推荐如此
this.setState(function(prevState, props) { return { age: prevState.age + 1 }; });
React推荐将大部件划分为一个个小部件,解耦。而组件的定义,常见的有三种方式
使用函数的方式定义,它的特色是无状态,实际上它并无被实例化,因此没法访问this对象,不能管理生命周期
多用于纯展现的组件
function Info(props) { return <p>{props.name}</p> } ReactDOM.render(<Info name="Jack" />, document.getElementById('box'));
函数组件接受一个属性参数,可直接获取
这种方式看起来像是ES5的形式,较广泛,根据官方说明,将被类形式取代
var Info = React.createClass({ getInitialState: function() { return { name: 'myName' }; }, render: function() { return <p>{this.state.name}</p> } });
在其中也可使用ES6的语法,为了和类形式的作些区别,代码多写了点
let Info = React.createClass({ getInitialState() { return { name: this.props.name || 'myName' }; }, getDefaultProps() { return { year: new Date().getFullYear() }; }, showYear(e) { console.log(this); let elem = ReactDOM.findDOMNode(e.target); console.log('year ' + elem.getAttribute('data-year')); }, render() { return <p onClick={this.showYear} data-year={this.props.year}>{this.state.name}</p> } });
绑定了点击事件,在点击函数处理中能够直接取到该组件的this对象
extends一看就是ES6的类形式了,比较推荐使用
class Info extends React.Component { constructor(props) { super(props); this.state = { name: this.props.name || 'myName' }; } showYear(e) { console.log(this); let elem = ReactDOM.findDOMNode(e.target); console.log('year ' + elem.getAttribute('data-year')); } render() { return <p onClick={this.showYear} data-year={this.props.year}>{this.state.name}</p> } } Info.defaultProps = { year: new Date().getFullYear() }; ReactDOM.render(<Info />, document.getElementById('box'));
能够看到一些区别,初始化props与state的方式不同
ES5形式中是直接在函数中return的方式,ES6形式的state是在构造函数中直接初始化this.state,而props初始化则须要在外部进行
再看看点击事件,会发现输出的this为null,因在ES6的类形式中,React并不会自动绑定函数方法的this对象,须要自行绑定
通常来讲,有三种绑定方式
3.1 直接在构造函数中统一绑定
constructor(props) { super(props); this.state = { name: this.props.name || 'myName' }; this.showYear = this.showYear.bind(this); }
3.2 直接在onClick中绑定
相对在构造函数中绑定来讲,这种方法会更有针对性,不过多个统一绑定就会显得代码冗余
render() { return <p onClick={this.showYear.bind(this)} data-year={this.props.year}>{this.state.name}</p> }
3.3 在onClick绑定中使用回调函数调用
render() { return <p onClick={(e) => this.showYear(e)} data-year={this.props.year}>{this.state.name}</p> }
这种方式须要手动传入event参数,而上述两种不须要
图片引自:组件的生命周期
React的组件有从产生到消亡,有个生命周期。宏观来说有三个时期
实例化这个时期主要是组件的初始实例化阶段,如图
主要包括属性和状态的初始化阶段、组件即将加载(componentWillMount)阶段、组件渲染(render)阶段、组件加载完成(componentDidMount)阶段
除了render可在存在期的时候再次进行组件渲染以外,其余阶段只会发生一次
class Info extends React.Component { constructor(props) { super(props); this.state = { name: this.props.name, age: 0 }; } // 组件将加载 componentWillMount() { console.log('componentWillMount: ', this.state.age) } // 组件加载完成 componentDidMount() { console.log('componentDidMount: ', this.state.age) } // 渲染 render() { console.log('Info render: ', this.state.age); return <p>{this.state.name} {this.state.age}</p> } } ReactDOM.render(<Info name="Jack" />, document.getElementById('box'));
组件实例化以后,在组件存在的时期,随着与用户的交互,属性或状态的改变,组件可发生一些更新,如图
componentWillReceiveProps(nextProps)
组件接收到属性(一般是父级传来的),带一个参数,即为该属性对象
shouldComponentUpdate(nextProps, nextState)
组件是否应该更新,true|false,默认返回true,带两个参数,将要更新的属性对象和状态对象
须要注意的是,若是自定义了这个方法,就会直接覆盖默认的方法(若定义以后不返回则表示返回了false)
componentWillUpdate(nextProps, nextState)
组件将更新,带两个参数,将要更新的属性对象和状态对象
render
再次进入渲染阶段
componentDidUpdate(prevProps, prevState)
组件更新完成,带两个参数,以前(已经)更新的属性对象和状态对象
在这个时期,各个阶段按照流程不断地进行着,举个栗子
这里定义一个父组件InfoWrap和子组件Info
在实际开发中,为了防止JS阻塞HTML结构的渲染,初始异步获取数据时通常会放到componentDidMount中
class InfoWrap extends React.Component { constructor(props) { super(props); this.state = { name: 'defaultName' }; } componentDidMount() { setTimeout(() => { this.setState({ name: 'Jack' }); }, 1000); setTimeout(() => { this.setState({ name: 'Jack' }); }, 3000); } render() { console.log('InfoWrap render'); return <Info name={this.state.name} /> } } ReactDOM.render(<InfoWrap />, document.getElementById('box'));
经过setTimeout模拟异步,一段时间后改变状态state中的name值,经过属性name传入子Info组件中
这里要注意的是,两次setState的name值相同,
基于React依照state状态的diff来判断是否须要从新渲染数据,在InfoWrap中不会更新两次HTML,但仍是会向子Info中传入两次属性props
class Info extends React.Component { constructor(props) { super(props); this.state = { name: this.props.name, age: 0 }; } increaseAge() { this.setState({ age: this.state.age + 1 }); } // 组件将加载 componentWillMount() { console.log('componentWillMount: ', this.state.age) } // 组件加载完成 componentDidMount() { console.log('componentDidMount: ', this.state.age) } // 组件接收到新的props componentWillReceiveProps(nextProps) { if (nextProps.name !== this.state.name) { this.setState({ name: nextProps.name }); } console.log('componentWillReceiveProps: ', nextProps) } // 组件是否应该更新 shouldComponentUpdate(nextProps, nextState) { console.log('shouldComponentUpdate: ', nextProps, nextState); // return nextProps.name !== this.state.name; return nextState.age !== 3; } // 组件将更新 componentWillUpdate(nextProps, nextState) { console.log('componentWillUpdate: ', this.state.age) } // 组件更新完成 componentDidUpdate(prevProps, prevState) { console.log('componentDidUpdate: ', this.state.age) } // 组件将移除 componentWillUnmount() { console.log('componentWillUnmount: ', this.state.age) } // 渲染 render() { console.log('Info render: ', this.state.age); // 在这更改状态将会无限循环 // this.setState({ // age: this.state.age + 1 // }); return <p onClick={this.increaseAge.bind(this)} >{this.state.name} {this.state.age}</p> } }
由上图,子Info被渲染了三次,而实际上第三次name并未改变,实际上是不须要渲染的
在实际开发中,为了防止无心义的渲染,一般会在shouldComponentUpdate添加判断,自定义是否须要更新
将其中的return nextProps.name !== this.state.name;取消注释,则再也不被更新渲染
细心点能够看到,Info组件中的setState是放在了componentWillReceiveProps中
为何不直接在shouldComponentUpdate中判断是否须要更新后再更新状态呢?
根据上方的流程图,若是在这里更新,就会再次触发state改变,致使又多循环执行了一次
因此通常的作法是在componentWillReceiveProps中根据条件判断是否须要更新状态,而后在shouldComponentUpdate中再根据条件判断是否须要更新渲染组件
同理,千万不要在render的时候setState更新状态,这更危险,会出现死循环,不注意的话能够直接把浏览器搞崩了
以上是子组件从父组件获取数据后更新的状况,下面来看看在子组件中的自我更新(increaseAge方法)
假设如今点击一次age属性值自增一次,在age不等于3的时候才更新页面
能够看到,在render和componentDidUpdate阶段,state的值才被实实在在地更新了,因此在以前的阶段取setState以后的新值,仍为旧的值
销毁期发生在组件被移除的时候,用于若是卸载组件后须要作一些特殊操做时,通常不多用
组件一多起来,就涉及到不一样组件之间的数据交流,主要有三种类型
React是单向的数据流动
父组件向子组件传递数据,其实就是经过props属性传递的方式,父组件的数据更新,经过props数据的流动,子组件也获得更新
子组件与父组件通讯,不一样于Angular.js的数据双向绑定,在React中默认支持子同步父的数据
若想实现父同步子的数据,则须要在子数据发生改变的时候,调用执行父props传来的回调,从而达到父的同步更新
class InputItem extends React.Component { constructor(props) { super(props); this.state = {}; } inputChange(e) { this.props.inputChange(e.target.value); } render() { return <p title={this.props.title}> [InputItem]-input: <input type="text" onChange={this.inputChange.bind(this)} /> </p> } } class Page extends React.Component { constructor(props) { super(props); this.state = { inputValue: '' }; } inputChange(inputValue) { this.setState({ inputValue, }); } render() { return ( <div> <p>[Page]-input: <input type="input" value={this.state.inputValue} /></p> <InputItem title="myInput" inputChange={this.inputChange.bind(this)} /> <InputItem title="myInput" inputChange={this.inputChange.bind(this)} /> </div> ) } } ReactDOM.render(<Page />, document.getElementById('box'));
这里定义了一个父组件Page,子组件InputItem
在父组件中<InputItem title="myInput" ... /> 其实就有了父与子的通讯(props传递)
Page向InputItem传递了一个回调属性,InputItem数据改变后调用此回调,数据获得更新
上述是父同步子的数据,若是要实现兄弟之间(或者两个没什么关系的组件)的数据同步,就得结合父与子、子与父的方式
class InputItem extends React.Component { constructor(props) { super(props); this.state = {}; } inputChange(e) { this.props.inputChange(e.target.value); } render() { return <p title={this.props.title}> [InputItem]-input: <input type="text" onChange={this.inputChange.bind(this)} value={this.props.inputValue} /> </p> } } class Page extends React.Component { constructor(props) { super(props); this.state = { inputValue: '' }; } inputChange(inputValue) { this.setState({ inputValue, }); } render() { return ( <div> <p>[Page]-input: <input type="input" value={this.state.inputValue} /></p> <InputItem title="myInput" inputChange={this.inputChange.bind(this)} inputValue={this.state.inputValue} /> <InputItem title="myInput" inputChange={this.inputChange.bind(this)} inputValue={this.state.inputValue} /> </div> ) } } ReactDOM.render(<Page />, document.getElementById('box'));
子InputItem更新后,调用父Page的回调,在父Page中将更新后的数据经过props传至子InputItem
不一样组件之间数据获得同步
这个还没用过 不清楚..
在React中的表单Form系统中,有受控组件与非受控组件一说
非受控,即表单项的value不受React的控制,不设初始value值,咱们能够随意更改
但不便于统一使用React进行管理,也不便于设置初始值
class Page extends React.Component { constructor(props) { super(props); this.state = { inputValue: '' }; } inputChange(e) { console.log(e.target.value) } render() { return ( <div> <p><input type="input" onChange={this.inputChange.bind(this)} /></p> </div> ) } } ReactDOM.render(<Page />, document.getElementById('box'));
能够看到,此input项目彷佛与React没什么关系,想获取它的值就必须经过DOM获取到该元素,不方便管理
受控组件,是为了更好地管理表单项的值
但要注意的是,一旦设置了value,将不能经过直接在表单项输入就能改变value值
由于value已经被React控制,要更新value值,就得更新相应的state状态值
对于受控组件,又有初始值和值两种之分
2.1 初始值(defaultValue) -- 注:其实defaultValue应该是属于非受控组件的
defaultValue这里指的是input,select,textarea等,相应的checkbox radio是defaultChecked
初始值只是初始的一个值,在第一次设置定义以后就不可改变
在实际开发中,数据的获取常常是异步的,大部分状况下会先初始设置input表单值为空,获取到数据后再放到input中(如编辑页面)
便会有如下代码
class InputItem extends React.Component { constructor(props) { super(props); this.state = { inputValue: this.props.inputValue || '' }; } componentWillReceiveProps(nextProps) { this.setState({ inputValue: nextProps.inputValue }); } inputChange(e) { let inputValue = e.target.value; console.log(inputValue); // this.setState({ // inputValue // }); } render() { return <p><input type="input" onChange={this.inputChange.bind(this)} defaultValue={this.state.inputValue} /></p> } } class Page extends React.Component { constructor(props) { super(props); this.state = { inputValue: '' }; } componentDidMount() { setTimeout(() => { this.setState({ inputValue: 'myValue' }); }, 1000); } render() { return <InputItem inputValue={this.state.inputValue} /> } } ReactDOM.render(<Page />, document.getElementById('box'));
初始在InputItem中设置了defaultValue为空,一段时间后获取到父Page传来的新值inputValue,然而InputItem中的defaultValue并不会更新
这种状况,就不适用与defaultValue了,换成用状态控制的value便可
2.2 值(value)
render() { return <p><input type="input" onChange={this.inputChange.bind(this)} value={this.state.inputValue} /></p> }
获取到异步的数据后,经过componentWillReceiveProps中更新状态值
加入onChange事件,在输入的时候更新状态值
而对于onChange事件的调用更新state,也有点点小技巧
假如input项目太多,为每一个input定义一个change回调并不实际
这时能够在bind中指定参数,指定是某个input项,或者直接在input项中添加属性区分,调用的时候再获取
class InputItem extends React.Component { constructor(props) { super(props); this.state = { userName: this.props.userName || '', age: this.props.age || '' }; } componentWillReceiveProps(nextProps) { this.setState({ userName: nextProps.userName, age: nextProps.age }); } inputChange(name, e) { this.setState({ [name]: e.target.value }); } // inputChange(e) { // this.setState({ // [e.target.getAttribute('name')]: e.target.value // }); // } render() { return ( <div> <p><input type="input" name="userName" onChange={this.inputChange.bind(this, 'userName')} value={this.state.userName} /></p> <p><input type="input" name="age" onChange={this.inputChange.bind(this, 'age')} value={this.state.age} /></p> </div> ) } } class Page extends React.Component { constructor(props) { super(props); this.state = { userName: '', age: '' }; } componentDidMount() { setTimeout(() => { this.setState({ userName: 'Jack', age: 10 }); }, 1000); } render() { return <InputItem userName={this.state.userName} age={this.state.age} /> } } ReactDOM.render(<Page />, document.getElementById('box'));
默认状况下,若是bind中不填第二个参数,在回调中第一个参数就是触发的event对象
若是有第二个参数,回调中的第一个参数就是该参数,后续的参数才是触发的event对象
上述两个inputChange方法调用以后结果同样,这里也利用了ES6支持对象属性名为变量的新特性
另外,因为设置了value值以后的React组件表单项不能直接更改value值,须要修改state相应值。
在使用一些插件的时候可能会遇到问题,如日期插件bootstrap-datepicker
class DatePicker extends React.Component { constructor(props) { super(props); this.state = { timeFrom: '', timeEnd: '' }; } combindDate(date) { let year = date.getFullYear(), month = date.getMonth() + 1, day = date.getDate(); month = month < 10 ? '0' + month : month; day = day < 10 ? '0' + day : day; return [year, month, day].join('-'); } componentDidMount() { let $timeFrom = $(this.refs.timeFrom); $timeFrom.datepicker({ format: 'yyyy-mm-dd', autoclose: true, language: 'zh-CN' }).on('changeDate', (ev) => { let day = ev.date.getDate(); if (day > 15) { $timeFrom.datepicker('update', ''); // this.setState({ // timeFrom: '' // }); } else { // this.setState({ // timeFrom: this.combindDate(ev.date) // }); } }); } render() { return ( <div> <p>timeFrom: <input type="input" ref="timeFrom" value={this.state.timeFrom} /></p> <p>timeEnd: <input type="input" ref="timeEnd" value={this.state.timeEnd} /></p> </div> ) } } ReactDOM.render(<DatePicker />, document.getElementById('box'));
且看看这个timeFrom,假设如今的需求是选择的日期不能大于15号
正常状况下,直接调用.datepicker('update', '');清空便可
但在React受控组件中,这关乎状态state值,因此要同时进行显示地setState(包括选成功的赋值与选失败的清空,即注释部分)
组件的复制也是一块知识,不过我这里应该不算是复制吧,其实只是一个具体的栗子
想象一下有这么一个需求:
有不少道题,每道题会有一些附加的文件,须要有个文件的轮播,另外点击文件还有弹窗预览,弹窗中下方是文件轮播,上方是文件的预览轮播
因此一个页面会出现多个类似的轮播,点击轮播中的文件可弹窗预览该文件,在弹窗中下方还有这个类似的轮播
因此要作的其实就是三个组件,页面组件,文件轮播组件,弹窗预览组件(该组件中使用一个文件轮播组件)
思路很清晰,不过在实现过程当中发现,并非想象的样子,弹窗中的文件轮播组件并非在弹窗以后才加载,实际上是页面加载出来就加载了。
那例子太复杂,用几个input项模拟一下吧
Page组件是页面组件,InputItem是共享的,BoxBanner是弹窗组件
class InputItem extends React.Component { constructor(props) { super(props); this.state = { inputIndex: this.props.inputIndex || 0, inputValue: this.props.inputValue || '' }; } componentWillReceiveProps(nextProps) { this.setState({ inputIndex: nextProps.inputIndex, inputValue: nextProps.inputValue }); } componentDidMount() { console.log('componentDidMount ', this.state.inputIndex); } inputChange(e) { this.setState({ inputValue: e.target.value }); } inputClick() { console.log('inputClick'); } render() { return <p data-first="1" className="check-first">{this.state.inputIndex}、 <input type="input" onChange={this.inputChange.bind(this)} onClick={this.inputClick.bind(this)} value={this.state.inputValue} style={{'margin': '10px'}} /> </p> } } class BoxBanner extends React.Component { constructor(props) { super(props); this.state = { inputIndex: 0, inputValue: '' }; } openBox(e) { let elem = e.target; if (elem.tagName !== 'BUTTON') { return; } this.setState({ inputIndex: elem.getAttribute('data-index'), inputValue: elem.getAttribute('title') }); layer.open({ type: 1, title: false, shadeClose: true, // content: $('.template-box').html(), content: $('.template-box'), // content: $(this.refs.templateBox), success: function(layero) { let $first = $(layero).find('.check-first'); console.log('isFirst: ', $first.attr('data-first')); $first.attr('data-first', '0'); }.bind(this), end: function(layero) { // $('.check-first').attr('data-first', '1'); } }); } render() { return ( <div> <p onClick={this.openBox.bind(this)}> <button data-index="1" title="box1">box1</button> <button data-index="2" title="box1">box2</button> <button data-index="3" title="box1">box3</button> </p> <div className="template-box" ref="templateBox" style={{display: 'none'}}> <InputItem inputIndex={this.state.inputIndex} inputValue={this.state.title} /> </div> </div> ) } } class Page extends React.Component { constructor(props) { super(props); } render() { return ( <div> <BoxBanner /> </div> ) } } ReactDOM.render(<Page />, document.getElementById('box'));
这里有个要求是,判断是不是首次弹窗进来,初始设置data-first属性为1,弹窗后即更新为0
在BoxBanner组件中引入了一个InputItem组件,但InputItem组件被共享,只在页面开始加载是被加载了
传递到layer中的content彷佛只是加载后的结果,能够看到isFirst值不是预想的
在layer的content中指定InputItem组件明显是不可行的,毕竟这是JSX
因此,就得在弹窗关闭以后恢复相关的值,即end回调中的注释部分
上述的代码中
// content: $('.template-box').html(), content: $('.template-box'), // content: $(this.refs.templateBox),
最开始用的是第一种方法,但这将只会传递html,其中的事件将不被执行
换成第二种,事件的传递获得解决,但在React中过多的DOM操做并不推荐,且若是存在多个.template-box时,基于弹窗中组件不会从新加载的问题,组件的获取就不正确
建议是换成第三种,取该组件的ref映射
Page组件中加多一项
render() { return ( <div> <BoxBanner /> <BoxBanner /> </div> ) }