React 基础实例教程

前端之家收集整理的这篇文章主要介绍了React 基础实例教程前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

一、基本使用

1. 同一页面中使用

首先,需要核心库react.js与React的DOM操作组件react-dom.js

其次,如果需要在当前HTML页面中直接写react的代码,就要引入browser.js文件,用于解析相关的JSX语法,同时,script标签指定好type

引入browser是为了在浏览器端能直接解析JSX,不过相当耗时,所以建议在本地解析之后再引入ES5的语法文件。当然,JSX语法是不必要的,只是推荐使用。

通过ReaactDOM.render方法渲染,参数1指定组件,参数2指定标签元素

2. 独立文件中使用

使用babel工具对文件进行解析,@H_301_43@Sublime Text中怎么配置babel编译?

查看编译后的文件

可以看到,JSX语法,核心就是React的createElement方法,我可以也直接使用这个方法创建。

这一丁点代码就编译了那么久,确实应该在本地先编译好

除了直接在浏览器引入react和react-dom之外,既然需要本地先编译,也可以使用构建工具如Webpack,不仅支持ES6与JSX的解析,还提供了一系列如代码压缩文件合并的功能,也便于管理,不必每次都得手动编译

可以通过npm工具安装reactreact-dom包后,引入直接使用(需要ES6基础)

这里就不展开说明了,有兴趣的可以自行去查查相关用法

二、JSX

JSX是React中和重要的部分,直观的表现是将HTML嵌入到了JS中,通过工具(如Babel)编译成支持的JS文件

复制代码

  1. varInfo=React.createClass({
  2. render:function(){return<pclassName="user">{this.props.name}</p>}
  3. });
  4.  
  5. ReactDOM.render(<Infoname="Jack"/>,document.getElementById('Box')
  6. );

复制代码

可以看到,return关键字后接上一个<p>标签,其中使用{}置入了JS语法。

1. 需要注意的是,return后面只能有一个父级标签

复制代码

  1. varInfo=React.createClass({
  2. render:function(){return<pclassName="user">
  3. {this.props.name=='Jack'?
  4. <span>isJack</span>
  5. :''
  6. }</p>}
  7. });

复制代码

2. {}中可以嵌入JS表达式,常见的是三目运算符与map操作

需要注意的是,三目运算符之后也只能接一个父级的标签,否则会报错

还可以置入组件

复制代码

  1. varJack=React.createClass({
  2. render:function(){return<p>I'mJack</p>
  3. }
  4. });
  5. varPual=React.createClass({
  6. render:function(){
  7. return<p>I'mPual</p>}
  8. });varInfo=React.createClass({
  9. render:function(){return(<divclassName="user">
  10. {this.props.name=='Jack'?
  11. <Jack/>:<Pual/>}</div>)
  12. }
  13. });
  14.  
  15. ReactDOM.render(<Infoname="Pual"/>,51);" name="two-3">3. 在JSX中,HTML的属性是受限的
  16. 在HTML标签中使用非原始HTML支持属性(可加前缀data-),会被React忽略,class关键字需要换成className

  17. 事件绑定需要使用camelCase形式(如onClick)

  18. varInfo=React.createClass({
  19. render:function(){return<pclassName="user"me="me"name="myName">{this.props.name}</p>}
  20. });
  21. 4. 智能的...展开操作符

  22. JSX支持ES6中很多语法,包括...这个东西。有时不想一个个属性写,为了代码美观,可以使用

  23. 复制代码

  24. varInfo=React.createClass({
  25. render:function(){varmyAttr={'title':'myTitle','age':10,'data-age':10,'onClick':function(){
  26. console.log('onClick');
  27. },'onclick':function(){
  28. console.log('onclick');
  29. }
  30. }return<pclassName="user"me="me"{...myAttr}>{this.props.name}</p>}
  31. });
  32. ReactDOM.render(<Infoname="Jack"/>,255);">编译后将自动展开,其中age被忽略,data-age被保留,onclick被忽略,onClick被保留

  33. 5. 事件的绑定与event对象传值

  34. 由于React对事件的绑定处理忽略了原始支持onclick属性,在使用其他JS库时,可能会遇到问题

  35. WdatePicker日期插件,它的使用方式是直接在HTML中绑定

  36. <inputtype="text"name=""onclick="WdatePicker()"/>
  37. <inputtype="text"name=""onClick="WdatePicker()"/>
  38. 但转到React中就不适用了,onclick会直接被忽略,onClick因为传的不是函数也被忽略,所以需要换个法子

  39. 复制代码

  40. render(){//return<inputtype="text"name=""onclick="WdatePicker()"/>
  41. //return<inputtype="text"name=""onClick="WdatePicker()"/>
  42. letclickEvent={
  43. onClick:function(e){
  44. console.log(e);
  45. WdatePicker(e);
  46. }
  47. };return<inputtype="text"name="date"ref="date"{...clickEvent}/>
  48. }
  49. 这样一来就能绑定上事件,此日期插件需要一个event对象,然而点击后报错了,调试输出该对象似乎有些奇特

  50. 再换种方式,在组件渲染之后直接绑定,成功

  51. 复制代码

  52. componentDidMount(){
  53. letdate=ReactDOM.findDOMNode(this.refs.date);
  54. date.onclick=function(e){
  55. console.log(e);
  56. WdatePicker(e);
  57. }
  58. }
  59. 虽说这是插件使用方式的不合理,但React传过来的event对象也已经不是原始的event对象了

  60. 6. 支持自闭合的标签,要显示地给它关闭

  61. 举个例子,对于<input>标签

  62. <inputtype="text">
  63. 一般的HTML中这样是支持的,但在JSX中会报错

  64. 需要加个斜杠,同理用于<img>等标签

  65. <inputtype="text"/>
  66. 三、属性、状态

  67. React中有属性与状态之分,都是为了方便存储或管理数据

  68. 1. 属性props

  69. 一旦定义,就不再改变的数据

  70. 一般来说,会通过在HTML标签添加属性的方式,让子组件获取到该props

  71. ReactDOM.render(<Infoname="Jack"/>,document.getElementById('Box')
  72. );
  73. Info组件中就可以通过this.props.name获取到该属性

  74. 也可以在组件中自己定义初始的属性,如果父有传name属性,则该初始属性被覆盖

  75. getDefaultProps:function(){return{
  76. name:'defaultName'
  77. };
  78. }
  79. 还可以定义属性的类型,是否必须

  80. propTypes:{
  81. name:React.PropTypes.string.isrequired
  82. }
  83. 这里定义了name属性必须有且为字符串,假设传入的是number类型(注意使用{}包裹,否则始终是字符串),则有警告

  84. ReactDOM.render(<Infoname={10}/>,255);">

    3012885-958514224.png">

  85. 虽然有修改props方法,但不建议对props进行修改,如果要修改,就使用state

  86. 2. 状态(state

  87. 状态是React中定义之后可改变的数据,只能在组件内部定义

  88. getInitialState:function(){return{
  89. age:10
  90. };
  91. }
  92. 在需要修改状态的时候,调用this.setState()方法即可(注意不能直接设置this.state = newObj

  93. this.setState({
  94. age:this.state.age+1});
  95. 注意必须初始化state对象,即初始化时至少要返回一个空的state对象,age属性的初始化是不必要的,只是为了便于管理

  96. ReactsetState方法是异步的,在其中取state.age可能取不到预期的值(不过目前还没遇到过)

  97. 这里的异步包含了两个概念

  98. 2.1 调用的时机异步

  99. React的组件有生命周期,在componentWillUpdaterender这两个时期之间才会调用

  100. 2.2 调用之后的异步

  101. setState实际上是一个异步方法,可带两个参数

  102.     this.setState({
  103. age:this.state.age+1
  104. },function(){
  105. });
  106. 更好的做法是直接在第一个参数使用函数,如此便保证了函数内部能取到正确的值,在大型复杂的组件中推荐如此

  107. this.setState(function(prevState,props){return{
  108. age:prevState.age+1
  109. };
  110. });
  111. @H_35_301@四、组件的三种定义方式 
  112. React推荐将大部件划分为一个个小部件,解耦。而组件的定义,常见的有三种方式

  113. 1. 函数式定义

  114. 使用函数的方式定义,它的特点是无状态,实际上它并没有被实例化,所以无法访问this对象,不能管理生命周期

  115. 多用于纯展示的组件

  116. functionInfo(props){return<p>{props.name}</p>}
  117. ReactDOM.render(<Infoname="Jack"/>,document.getElementById('Box'));
  118. 函数组件接受一个属性参数,可直接获取

  119. 2. React.createClass方式定义

  120. 这种方式看起来像是ES5的形式,较普遍,根据官方说明,将被类形式取代

  121. 复制代码

  122. varInfo=React.createClass({
  123. getInitialState:function(){return{
  124. name:'myName'
  125. };
  126. },render:function(){return<p>{this.state.name}</p>}
  127. });
  128. 在其中也可以使用ES6的语法,为了和类形式的做些区别,代码多写了点

  129. 复制代码

  130. letInfo=React.createClass({
  131. getInitialState(){return{
  132. name:this.props.name||'myName'
  133. };
  134. },getDefaultProps(){return{
  135. year:newDate().getFullYear()
  136. };
  137. },showYear(e){
  138. console.log(this);
  139. letelem=ReactDOM.findDOMNode(e.target);
  140. console.log('year'+elem.getAttribute('data-year'));
  141. },render(){return<ponClick={this.showYear}data-year={this.props.year}>{this.state.name}</p>}
  142. });
  143. 绑定了点击事件,在点击函数处理中可以直接取到该组件的this对象

  144. 3. extends React.Component方式定义

  145. extends一看就是ES6的类形式了,比较推荐使用

  146. 复制代码

  147. classInfoextendsReact.Component{
  148. constructor(props){
  149. super(props);this.state={
  150. name:this.props.name||'myName'
  151. };
  152. }
  153. showYear(e){
  154. console.log(this);
  155. letelem=ReactDOM.findDOMNode(e.target);
  156. console.log('year'+elem.getAttribute('data-year'));
  157. }
  158. render(){return<ponClick={this.showYear}data-year={this.props.year}>{this.state.name}</p>}
  159. }
  160. Info.defaultProps={
  161. year:newDate().getFullYear()
  162. };
  163. ReactDOM.render(<Info/>,document.getElementById('Box'));
  164. 可以看到一些区别,初始化propsstate的方式不一样

  165. ES5形式中是直接在函数return的方式,ES6形式的state是在构造函数中直接初始化this.state,而props初始化则需要在外部进行

  166. 再看看点击事件,会发现输出thisnull,因在ES6的类形式中,React并不会自动绑定函数方法this对象,需要自行绑定

  167. 一般来说,有三种绑定方式

  168. 3.1 直接在构造函数中统一绑定

  169. 复制代码

  170. constructor(props){
  171. super(props);this.state={
  172. name:this.props.name||'myName'
  173. };this.showYear=this.showYear.bind(this);
  174. }
  175. 3.2 直接在onClick中绑定

  176. 相对在构造函数中绑定来说,这种方法会更有针对性,不过多个统一绑定就会显得代码冗余

  177. render(){return<ponClick={this.showYear.bind(this)}data-year={this.props.year}>{this.state.name}</p>
  178. }
  179. 3.3 onClick绑定中使用回调函数调用

  180. render(){return<ponClick={(e)=>this.showYear(e)}data-year={this.props.year}>{this.state.name}</p>
  181. }
  182. 这种方式需要手动传入event参数,而上述两种不需要

  183. 五、组件的生命周期

  184. 图片引自:组件的生命周期

  185. React的组件有从产生到消亡,有个生命周期。宏观来讲有三个时期

  186. 1. 实例化期(Mounting

  187. 实例化这个时期主要是组件的初始实例化阶段,如图

  188. 30114468.png">

  189. 主要包括属性和状态的初始化阶段、组件即将加载(componentWillMount)阶段、组件渲染(render)阶段、组件加载完成(componentDidMount)阶段

  190. 除了render可在存在期的时候再次进行组件渲染之外,其他阶段只会发生一次

  191. 复制代码

  192. classInfoextendsReact.Component{
  193. constructor(props){
  194. super(props);this.state={
  195. name:this.props.name,age:0
  196. };
  197. }//组件将加载componentWillMount(){
  198. console.log('componentWillMount:',this.state.age)
  199. }//组件加载完成componentDidMount(){
  200. console.log('componentDidMount:',this.state.age)
  201. }//渲染render(){
  202. console.log('Inforender:',this.state.age);return<p>{this.state.name}{this.state.age}</p>}
  203. }
  204. ReactDOM.render(<Infoname="Jack"/>,255);">

  205. 2. 存在期间(Updating

  206. 组件实例化之后,在组件存在的时期,随着与用户的交互,属性或状态的改变,组件可发生一些更新,如图

  207. componentWillReceiveProps(nextProps)

  208. 组件接收到属性(通常是父级传来的),带一个参数,即为该属性对象

  209. shouldComponentUpdate(nextProps,nextState)

  210. 组件是否应该更新,true|false,默认返回true,带两个参数,将要更新的属性对象和状态对象

  211. 需要注意的是,如果自定义了这个方法,就会直接覆盖默认的方法(若定义之后不返回则表示返回了false

  212. componentWillUpdate(nextProps,255);">组件将更新,带两个参数,将要更新的属性对象和状态对象

  213. render

  214. 再次进入渲染阶段

  215. componentDidUpdate(prevProps,prevState)

  216. 组件更新完成,带两个参数,之前(已经)更新的属性对象和状态对象

  217. 在这个时期,各个阶段按照流程不断地进行着,举个栗子

  218. 这里定义一个父组件InfoWrap和子组件Info

  219. 在实际开发中,为了防止JS阻塞HTML结构的渲染,初始异步获取数据时一般会放到componentDidMount

  220. 复制代码

  221. classInfoWrapextendsReact.Component{
  222. constructor(props){
  223. super(props);this.state={
  224. name:'defaultName'
  225. };
  226. }
  227. componentDidMount(){
  228. setTimeout(()=>{this.setState({
  229. name:'Jack'
  230. });
  231. },1000);
  232. setTimeout(()=>{this.setState({
  233. name:'Jack'
  234. });
  235. },3000);
  236. }
  237. render(){
  238. console.log('InfoWraprender');return<Infoname={this.state.name}/>}
  239. }
  240. ReactDOM.render(<InfoWrap/>,255);">通过setTimeout模拟异步,一段时间后改变状态state中的name值,通过属性name传入子Info组件中

  241. 这里要注意的是,两次setStatename值相同,

  242. 基于React依照state状态的diff来判断是否需要重新渲染数据,在InfoWrap中不会更新两次HTML,但还是会向子Info中传入两次属性props

  243. 由上图,子Info被渲染了三次,而实际上第三次name并未改变,其实是不需要渲染的

  244. 在实际开发中,为了防止无意义的渲染,通常会在shouldComponentUpdate添加判断,自定义是否需要更新

  245. 将其中的return nextProps.name !== this.state.name;取消注释,则不再被更新渲染

  246. 细心点可以看到,Info组件中的setState是放在了componentWillReceiveProps

  247. 为什么不直接在shouldComponentUpdate中判断是否需要更新后再更新状态呢?

  248. 根据上方的流程图,如果在这里更新,就会再次触发state改变,导致又多循环执行了一次

  249. 所以一般的做法是在componentWillReceiveProps中根据条件判断是否需要更新状态,然后在shouldComponentUpdate中再根据条件判断是否需要更新渲染组件

  250. 同理,千万不要在render的时候setState更新状态,这更危险,会出现死循环,不注意的话可以直接把浏览器搞崩了

  251. 以上是子组件从父组件获取数据后更新的情况,下面来看看在子组件中的自我更新(increaseAge方法)

  252. 假设现在点击一次age属性值自增一次,在age不等于3的时候才更新页面

  253. 可以看到,在rendercomponentDidUpdate阶段,state的值才被实实在在地更新了,所以在之前的阶段取setState之后的新值,仍为旧的值

  254. 3. 销毁期(Unmounting

  255. 销毁期发生在组件被移除的时候,用于如果卸载组件后需要做一些特殊操作时,一般很少用

  256. 六、组件间的通信

  257. 组件一多起来,就涉及到不同组件之间的数据交流,主要有三种类型

  258. 1. 父子通信

  259. React是单向的数据流动

  260. 父组件向子组件传递数据,其实就是通过props属性传递的方式,父组件的数据更新,通过props数据的流动,子组件也得到更新

  261. 2. 子父通信

  262. 子组件与父组件通信,不同于Angular.js的数据双向绑定,在React中默认支持子同步父的数据

  263. 若想实现父同步子的数据,则需要在子数据发生改变的时候,调用执行父props传来的回调,从而达到父的同步更新

  264. 复制代码

  265. classInputItemextendsReact.Component{
  266. constructor(props){
  267. super(props);this.state={};
  268. }
  269. inputChange(e){this.props.inputChange(e.target.value);
  270. }
  271. render(){return<ptitle={this.props.title}>
  272. [InputItem]-input:<inputtype="text"onChange={this.inputChange.bind(this)}/>
  273. </p>}
  274. }
  275. classPageextendsReact.Component{
  276. constructor(props){
  277. super(props);this.state={
  278. inputValue:''
  279. };
  280. }
  281. inputChange(inputValue){this.setState({
  282. inputValue,});
  283. }
  284. render(){return(<div>
  285. <p>[Page]-input:<inputtype="input"value={this.state.inputValue}/></p>
  286. <InputItemtitle="myInput"inputChange={this.inputChange.bind(this)}/>
  287. <InputItemtitle="myInput"inputChange={this.inputChange.bind(this)}/>
  288. </div>)
  289. }
  290. }
  291. ReactDOM.render(<Page/>,255);">这里定义了一个父组件Page,子组件InputItem

  292. 在父组件中<InputItem title="myInput" ... />其实就有了父与子的通信(props传递)

  293. Page向InputItem传递了一个回调属性,InputItem数据改变后调用此回调,数据得到更新

  294. 3. 兄弟通信

  295. 上述是父同步子的数据,如果要实现兄弟之间(或者两个没什么关系的组件)的数据同步,就得结合父与子、子与父的方式

  296. 复制代码

  297. classInputItemextendsReact.Component{
  298. constructor(props){
  299. super(props);this.state={};
  300. }
  301. inputChange(e){this.props.inputChange(e.target.value);
  302. }
  303. render(){return<ptitle={this.props.title}>
  304. [InputItem]-input:<inputtype="text"onChange={this.inputChange.bind(this)}value={this.props.inputValue}/>
  305. </p>}
  306. }
  307. classPageextendsReact.Component{
  308. constructor(props){
  309. super(props);this.state={
  310. inputValue:''
  311. };
  312. }
  313. inputChange(inputValue){this.setState({
  314. inputValue,});
  315. }
  316. render(){return(<div>
  317. <p>[Page]-input:<inputtype="input"value={this.state.inputValue}/></p>
  318. <InputItemtitle="myInput"inputChange={this.inputChange.bind(this)}inputValue={this.state.inputValue}/>
  319. <InputItemtitle="myInput"inputChange={this.inputChange.bind(this)}inputValue={this.state.inputValue}/>
  320. </div>)
  321. }
  322. }
  323. ReactDOM.render(<Page/>,255);">子InputItem更新后,调用Page的回调,在父Page中将更新后的数据通过props传至子InputItem

  324. 不同组件之间数据得到同步

  325. 4. 事件发布/订阅

  326. 这个还没用过 不清楚..

  327. 七、受控组件与非受控组件

  328. React中的表单Form系统中,有受控组件与非受控组件一说

  329. 1. 非受控组件

  330. 非受控,即表单项的value不受React的控制,不设初始value值,我们可以随意更改

  331. 但不便于统一使用React进行管理,也不便于设置初始值

  332. 复制代码

  333. classPageextendsReact.Component{
  334. constructor(props){
  335. super(props);this.state={
  336. inputValue:''
  337. };
  338. }
  339. inputChange(e){
  340. console.log(e.target.value)
  341. }
  342. render(){return(<div>
  343. <p><inputtype="input"onChange={this.inputChange.bind(this)}/></p>
  344. </div>)
  345. }
  346. }
  347. ReactDOM.render(<Page/>,255);">可以看到,此input项目似乎与React没什么关系,想获取它的值就必须通过DOM获取到该元素,不方便管理

  348. 2. 受控组件

  349. 受控组件,是为了更好地管理表单项的值

  350. 但要注意的是,一旦设置了value,将不能通过直接在表单项输入就能改变value值

  351. 因为value已经被React控制,要更新value值,就得更新相应的state状态值

  352. 对于受控组件,又有初始值和值两种之分

  353. 2.1 初始值(defaultValue) -- 注:其实defaultValue应该是属于非受控组件的

  354. defaultValue这里指的是input,select,0);">textarea等,相应的checkBoxradiodefaultChecked

  355. 初始值只是初始的一个值,在第一次设置定义之后就不可改变

  356. 在实际开发中,数据的获取经常是异步的,大部分情况下会先初始设置input表单值为空,获取到数据后再放到input中(如编辑页面

  357. 便会有以下代码

  358. 复制代码

  359. classInputItemextendsReact.Component{
  360. constructor(props){
  361. super(props);this.state={
  362. inputValue:this.props.inputValue||''
  363. };
  364. }
  365. componentWillReceiveProps(nextProps){this.setState({
  366. inputValue:nextProps.inputValue
  367. });
  368. }
  369. inputChange(e){
  370. letinputValue=e.target.value;
  371. console.log(inputValue);//this.setState({
  372. //inputValue
  373. //});}
  374. render(){return<p><inputtype="input"onChange={this.inputChange.bind(this)}defaultValue={this.state.inputValue}/></p>
  375. }
  376. }
  377. classPageextendsReact.Component{
  378. constructor(props){
  379. super(props);this.state={
  380. inputValue:''
  381. };
  382. }
  383. componentDidMount(){
  384. setTimeout(()=>{this.setState({
  385. inputValue:'myValue'
  386. });
  387. },1000);
  388. }
  389. render(){return<InputIteminputValue={this.state.inputValue}/>}
  390. }
  391. ReactDOM.render(<Page/>,255);">初始在InputItem中设置了defaultValue为空,一段时间后获取到父Page传来的新值inputValue,然而InputItem中的defaultValue并不会更新

  392. 这种情况,就不适用与defaultValue了,换成用状态控制的value即可

  393. 2.2 值(value)

  394. render(){return<p><inputtype="input"onChange={this.inputChange.bind(this)}value={this.state.inputValue}/></p>
  395. }
  396. 获取到异步的数据后,通过componentWillReceiveProps中更新状态值

  397. 加入onChange事件,在输入的时候更新状态值

  398. 而对于onChange事件的调用更新state,也有点点小技巧

  399. 假如input项目太多,为每个input定义一个change回调并不实际

  400. 这时可以在bind中指定参数,指定是某个input项,或者直接在input项中添加属性区分,调用的时候再获取

  401. 复制代码

  402. classInputItemextendsReact.Component{
  403. constructor(props){
  404. super(props);this.state={
  405. userName:this.props.userName||'',age:this.props.age||''
  406. };
  407. }
  408. componentWillReceiveProps(nextProps){this.setState({
  409. userName:nextProps.userName,age:nextProps.age
  410. });
  411. }
  412. inputChange(name,e){this.setState({
  413. [name]:e.target.value
  414. });
  415. }//inputChange(e){
  416. //this.setState({
  417. //[e.target.getAttribute('name')]:e.target.value
  418. //});
  419. //}
  420. render(){return(<div>
  421. <p><inputtype="input"name="userName"onChange={this.inputChange.bind(this,'userName')}value={this.state.userName}/></p>
  422. <p><inputtype="input"name="age"onChange={this.inputChange.bind(this,'age')}value={this.state.age}/></p>
  423. </div>)
  424. }
  425. }
  426. classPageextendsReact.Component{
  427. constructor(props){
  428. super(props);this.state={
  429. userName:'',age:''
  430. };
  431. }
  432. componentDidMount(){
  433. setTimeout(()=>{this.setState({
  434. userName:'Jack',age:10
  435. });
  436. },1000);
  437. }
  438. render(){return<InputItemuserName={this.state.userName}age={this.state.age}/>}
  439. }
  440. ReactDOM.render(<Page/>,255);">默认情况下,如果bind中不填第二个参数,在回调中第一个参数就是触发的event对象

  441. 如果有第二个参数,回调中的第一个参数就是该参数,后续的参数才是触发的event对象

  442. 上述两个inputChange方法调用之后结果一样,这里也利用了ES6支持对象属性名为变量的新特性

  443. 另外,由于设置了value值之后的React组件表单项不能直接更改value值,需要修改state相应值。

  444. 在使用一些插件的时候可能会遇到问题,如日期插件bootstrap-datepicker

  445. 复制代码

  446. classDatePickerextendsReact.Component{
  447. constructor(props){
  448. super(props);this.state={
  449. timeFrom:'',timeEnd:''
  450. };
  451. }
  452. combindDate(date){
  453. letyear=date.getFullYear(),month=date.getMonth()+1,day=date.getDate();
  454. month=month<10?'0'+month:month;
  455. day=day<10?'0'+day:day;return[year,month,day].join('-');
  456. }
  457. componentDidMount(){
  458. let$timeFrom=$(this.refs.timeFrom);
  459. $timeFrom.datepicker({
  460. format:'yyyy-mm-dd',autoclose:true,language:'zh-CN'
  461. }).on('changeDate',(ev)=>{
  462. letday=ev.date.getDate();if(day>15){
  463. $timeFrom.datepicker('update','');//this.setState({
  464. //timeFrom:''
  465. //});
  466. }else{//this.setState({
  467. //timeFrom:this.combindDate(ev.date)
  468. //});}
  469. });
  470. }
  471. render(){return(<div>
  472. <p>timeFrom:<inputtype="input"ref="timeFrom"value={this.state.timeFrom}/></p>
  473. <p>timeEnd:<inputtype="input"ref="timeEnd"value={this.state.timeEnd}/></p>
  474. </div>)
  475. }
  476. }
  477. ReactDOM.render(<DatePicker/>,255);">且看看这个timeFrom,假设现在的需求是选择的日期不能大于15号

  478. 正常情况下,直接调用.datepicker('update','');清空即可

  479. 但在React受控组件中,这关乎状态state值,所以要同时进行显示地setState(包括选成功的赋值与选失败的清空,即注释部分)

  480. 八、组件的复制

  481. 组件的复制也是一块知识,不过我这里应该不算是复制吧,其实只是一个具体的栗子

  482. 1. 弹窗中的组件并不是在弹窗之后才加载,其实是初始就加载

  483. 想象一下有这么一个需求:

  484. 有很多道题,每道题会有一些附加的文件,需要有个文件的轮播,另外点击文件还有弹窗预览,弹窗中下方是文件轮播,上方是文件的预览轮播

  485. 所以一个页面会出现多个相似的轮播,点击轮播中的文件可弹窗预览该文件,在弹窗中下方还有这个相似的轮播

  486. 所以要做的其实就是三个组件,页面组件,文件轮播组件,弹窗预览组件(该组件中使用一个文件轮播组件)

  487. 思路很清晰,不过在实现过程中发现,并不是想象的样子,弹窗中的文件轮播组件并不是在弹窗之后才加载,其实是页面加载出来就加载了。

  488. 那例子太复杂,用几个input项模拟一下吧

  489. Page组件是页面组件,InputItem是共享的,BoxBanner是弹窗组件

  490. 复制代码

  491. classInputItemextendsReact.Component{
  492. constructor(props){
  493. super(props);this.state={
  494. inputIndex:this.props.inputIndex||0,inputValue:this.props.inputValue||''
  495. };
  496. }
  497. componentWillReceiveProps(nextProps){this.setState({
  498. inputIndex:nextProps.inputIndex,inputValue:nextProps.inputValue
  499. });
  500. }
  501. componentDidMount(){
  502. console.log('componentDidMount',this.state.inputIndex);
  503. }
  504. inputChange(e){this.setState({
  505. inputValue:e.target.value
  506. });
  507. }
  508. inputClick(){
  509. console.log('inputClick');
  510. }
  511. render(){return<pdata-first="1"className="check-first">{this.state.inputIndex}、<input
  512. type="input"
  513. onChange={this.inputChange.bind(this)}
  514. onClick={this.inputClick.bind(this)}
  515. value={this.state.inputValue}
  516. style={{'margin':'10px'}}/>
  517. </p>}
  518. }
  519. classBoxBannerextendsReact.Component{
  520. constructor(props){
  521. super(props);this.state={
  522. inputIndex:0,inputValue:''
  523. };
  524. }
  525. openBox(e){
  526. letelem=e.target;if(elem.tagName!=='BUTTON'){return;
  527. }this.setState({
  528. inputIndex:elem.getAttribute('data-index'),inputValue:elem.getAttribute('title')
  529. });
  530. layer.open({
  531. type:1,title:false,shadeClose:true,//content:$('.template-Box').html(),content:$('.template-Box'),//content:$(this.refs.templateBox),success:function(layero){
  532. let$first=$(layero).find('.check-first');
  533. console.log('isFirst:',$first.attr('data-first'));
  534. $first.attr('data-first','0');
  535. }.bind(this),end:function(layero){//$('.check-first').attr('data-first','1');}
  536. });
  537. }
  538. render(){return(<div>
  539. <ponClick={this.openBox.bind(this)}>
  540. <buttondata-index="1"title="Box1">Box1</button>
  541. <buttondata-index="2"title="Box1">Box2</button>
  542. <buttondata-index="3"title="Box1">Box3</button>
  543. </p>
  544. <divclassName="template-Box"ref="templateBox"style={{display:'none'}}>
  545. <InputIteminputIndex={this.state.inputIndex}inputValue={this.state.title}/>
  546. </div>
  547. </div>)
  548. }
  549. }
  550. classPageextendsReact.Component{
  551. constructor(props){
  552. super(props);
  553. }
  554. render(){return(<div>
  555. <BoxBanner/>
  556. </div>)
  557. }
  558. }
  559. ReactDOM.render(<Page/>,255);">这里有个要求是,判断是否是首次弹窗进来,初始设置data-first属性1,弹窗后即更新为0

  560. BoxBanner组件中引入了一个InputItem组件,但InputItem组件被共享,只在页面开始加载是被加载了

  561. 传递到layer中的content似乎只是加载后的结果,可以看到isFirst值不是预想的

  562. layercontent中指定InputItem组件明显是不可行的,毕竟这是JSX

  563. 所以,就得在弹窗关闭之后恢复相关的值,即end回调中的注释部分

  564. 上述的代码

  565. //content:$('.template-Box').html(),
  566. 最开始用的是第一种方法,但这将只会传递html,其中的事件将不被执行

  567. 换成第二种,事件的传递得到解决,但在React中过多的DOM操作并不推荐,且如果存在多个.template-Box时,基于弹窗中组件不会重新加载的问题,组件的获取就不正确

  568. 建议是换成第三种,取该组件的ref映射

  569. Page组件中加多一项

  570. 复制代码

  571. render(){return(<div>
  572. <BoxBanner/>
  573. <BoxBanner/>
  574. </div>)
  575. }

猜你在找的React相关文章