Open ajccom opened 8 years ago
最近使用 React 尝试着做了一些组件,在这里和大家分享一下经验,也希望能对刚刚开始学习 React 的朋友有所帮助。
下文以制作一个组件为例,一步步的讲解组件从无到有的过程。
阅读文章后你可能会了解:
新建一份 HTML 文件,引入 react.js、react-dom.js。
react.js
react-dom.js
<script src="react.js"></script> <script src="react-dom.js"></script>
由于我选择使用 JSX 方式编写 React 代码,所以需要注意两个地方:
type="text/babel"
<!-- include browser.min.js --> <script src="browser.min.js"></script> <!-- set type="text/babel" --> <script type="text/babel" src="..."></script> <!-- or --> <script type="text/babel"> ... </script>
组件的功能很简单:
创建一个按钮,点击按钮显示/隐藏一个弹出层,弹出层可拖动。
首先我们来制作一个按钮,按钮很简单,只有一个功能:
直接上代码:
var ClickBtn = React.createClass({ getInitialState: function() { //设置按钮初始状态为 off return {status: 'off', klass: 'btn'} }, onClick: function () { this.setState({status: this.state.status === 'off' ? 'on' : 'off'}) }, render: function () { //返回 React Element return <div ref="btn" onClick={this.onClick} className={this.state.klass + ' ' + this.state.status} >Click Me</div> } })
React Element 对象的 className 属性决定了最后页面上元素的 class 属性值。这里为了按钮能够切换状态,除了固定的 btn 类之外,还给按钮设置了一个会变化的类。
btn
在代码的 render 方法出现了十分奇怪的语法,方法返回的是一个类似字符串的内容,却没有引号?
return <div ref="btn" onClick={this.onClick} className={this.state.klass + ' ' + this.state.status} >Click Me
这是因为具有 type="text/babel" 属性的 script 标签并不会被浏览器执行。JSX 会帮你编译这些文本为真正的 Javascript 并执行。所以这里奇怪的语法只是为了方便你写代码的模板而已。
//JSX return <div ref="btn" onClick={this.onClick} className={this.state.klass + ' ' + this.state.status} >Click Me</div> //javascript return React.createElement( "div", {ref: 'btn', onClick: this.onClick, className: this.state.klass + ' ' + this.state.status}, "Click Me" );
显然直接使用 createElement 方法创建 React Element 是比较繁琐的,所以我个人更加倾向于省时省力的 JSX 写法 :smile:。另外顺带一提的是,即使是使用 JSX ,你还是可以在模板中使用 React.createElement 方法哦,不过这有点画蛇添足了。
createElement
React.createElement
弹出层组件相比上面的按钮组件稍微复杂一点,我们需要多绑定一些事件。
简单地将以往的可拖拽弹出层用 React 实现一遍:
var dom = window.document var Popup = React.createClass({ getInitialState: function() { var self = this //为了拖拽过程流畅,所以在 document 上绑定滑动事件哦 dom.addEventListener('mousemove', function (e) {self.onMouseMove(e)}) dom.addEventListener('mouseup', function (e) {self.onMouseUp(e)}) return {status: 'block', left: 0, top: 0, startLeft: 0, startTop: 0, x: 0, y: 0, ready: false} }, onMouseDown: function (e) { var x = e.clientX, y = e.clientY this.setState({ready: true, x: x, y: y, startLeft: this.state.left, startTop: this.state.top}) }, onMouseMove: function (e) { //鼠标在弹出层上按住时才可以拖动 if (!this.state.ready) {return} var x = e.clientX, y = e.clientY this.setState(function (state) { return { left: state.startLeft + x - state.x, top: state.startTop + y - state.y } }) }, onMouseUp: function () { this.setState({ready: false}) }, render: function () { //行内样式可以使用变量放在 React Element 的 style 属性中,或者这样: // <div style={{left: 0, top: 0, display: 'none'}}></div> var style = { left: this.state.left, top: this.state.top, display: this.state.status } return ( <div ref="popup" onMouseDown={this.onMouseDown} className="popup" style={style} >Drag Me</div> ) } })
到这里我们已经成功创建了一个弹出层的组件。由于弹出层会随着鼠标的移动而改变它的样式,所以我在 React Element 中添加了行内样式,取值来自自身的状态。
剩下的工作就是如何整合两个组件了。我们可以按照以往的方法,给组件定义一些实用的外部接口。
组件一般都会提供外部接口以方便被调用,从而掩盖其内部实现机制。
先给按钮组件添加两个方法,分别是开和关:
// ClickBtn on: function () { this.setState({status: 'on'}) }, off: function () { this.setState({status: 'off'}) }
再给弹出层组件做一些修改,增加显示和隐藏的方法:
// Popup show: function () { this.setState({status: 'block'}) }, hide: function () { this.setState({status: 'none'}) }
上面的两个组件都可以独立完成工作,而且也已经有了十分方便的外部接口供调用。那么整合在一起后,它们之间该如何通信呢?我们可以简单的在创建 React Element 时增加 ref 属性达到目的:
ref
<ClickBtn ref="btn" /> <Popup ref="pop" />
这样我们就可以通过对象的 refs 属性获取引用的组件实例。
refs
console.log(this.refs.btn) //打印 ClickBtn 组件实例 console.log(this.refs.pop) //打印 Popup 组件实例
虽然 refs 这种方法简洁明地实现了父组件对子组件的调用,但是难以实现子组件对父组件的通知。所幸我们可以利用使用属性值传递进行父组件与子组件的通信,React 网站首页的示例中就是这么做的。
为了说明问题,我们将 popup 多增加一个功能,鼠标点击弹窗层外的区域则关闭弹出层。此时我们的新组件需要将按钮状态重置回 off 状态。简单的做如下修改:
off
//Popup //给 document 元素绑定的事件中增加判断是否点击了外部区域 onMouseUp: function (e) { var isNotify = false if (this.refs.popup !== e.target && this.state.status === 'block') { isNotify = true } isNotify && this.props.change(e) this.setState({ready: false}) } ------------------------------------------- //Component change: function (e) { //如果点到的是按钮组件,就不执行这个方法了,因为执行了 onClick 了 if (this.refs.btn.refs.btn !== e.target) { this.toggle() } }, render: function () { return ( <div className="component"> <div onClick={this.onClick} > <ClickBtn ref="btn" status={this.state.status} /> </div> <Popup ref="pop" change={this.change} /> </div> ) }
上面的代码中通过将新组件的 change 方法通过属性传递给 Popup 组件,然后 Popup 组件的在确认需要隐藏弹出层后利用 change 方法通知父组件。
change
Popup
接下来看下父组件的代码:
var Component = React.createClass({ getInitialState: function() { return {status: 'off'} }, onClick: function () { this.toggle() }, change: function () { //如果点到的是按钮组件,就不执行这个方法了,因为执行了 onClick 了 if (this.refs.btn.refs.btn !== e.target) { this.toggle() } }, render: function () { return ( <div className="component"> <div onClick={this.onClick} > <ClickBtn ref="btn" /> </div> <Popup ref="pop" change={this.change} /> </div> ) }, toggle: function () { var refs = this.refs this.setState({status: this.state.status === 'off' ? 'on' : 'off'}, function () { if (this.state.status === 'on') { refs.btn.on() refs.pop.show() } else { refs.btn.off() refs.pop.hide() } }) } })
利用两个子组件合成了 Component 组件后,我利用 refs 调用子组件的方法,利用属性传递实现子组件对父组件的通信。
Component
而对于外部的调用,则可以通过 ReactDOM.render 方法的返回值得到组件实例。
ReactDOM.render
以我们的 Component 组件为例:
window.component = ReactDOM.render(<Component />, document.getElementById('...')); //可以调用 Component 组件的方法啦 component.toggle()
查看 demo
使用 React 的感觉还是比较方便的,组件之间互相联系起来也很容易。最近看到很多使用 React 制作的组件库,相当值得借鉴学习,同时这也确实推动了我学习 React 的热情。
在以前学习 AngularJS 的时候,也有写过点击出现可拖拽弹出层的示例代码,这也是为什么我这次选用这个例子的原因。两种方案相较,感觉 React 写起来更快一点,这倒不是从技术层面的分析,仅仅是我刚刚熟悉了 React,而 AngularJS 已经好久没用了,不看文档是不会写的了 :stuck_out_tongue:。
今天需要用 React + Redux(Redux-form) 做表单提交之类的,同事直接用的 react-bootstrap 的库,太难看。正准备自己实现一个 模态框 就看到这篇文章了,写的挺好的
从零开始,用 React 写一个组件
最近使用 React 尝试着做了一些组件,在这里和大家分享一下经验,也希望能对刚刚开始学习 React 的朋友有所帮助。
下文以制作一个组件为例,一步步的讲解组件从无到有的过程。
阅读文章后你可能会了解:
准备工作
新建一份 HTML 文件,引入
react.js
、react-dom.js
。由于我选择使用 JSX 方式编写 React 代码,所以需要注意两个地方:
type="text/babel"
组件功能描述
组件的功能很简单:
第一步:制作按钮组件
首先我们来制作一个按钮,按钮很简单,只有一个功能:
直接上代码:
React Element 对象的 className 属性决定了最后页面上元素的 class 属性值。这里为了按钮能够切换状态,除了固定的
btn
类之外,还给按钮设置了一个会变化的类。在代码的 render 方法出现了十分奇怪的语法,方法返回的是一个类似字符串的内容,却没有引号?
这是因为具有
type="text/babel"
属性的 script 标签并不会被浏览器执行。JSX 会帮你编译这些文本为真正的 Javascript 并执行。所以这里奇怪的语法只是为了方便你写代码的模板而已。直接写 javascript 与使用 JSX 比较
显然直接使用
createElement
方法创建 React Element 是比较繁琐的,所以我个人更加倾向于省时省力的 JSX 写法 :smile:。另外顺带一提的是,即使是使用 JSX ,你还是可以在模板中使用React.createElement
方法哦,不过这有点画蛇添足了。第二步:制作弹出层
弹出层组件相比上面的按钮组件稍微复杂一点,我们需要多绑定一些事件。
简单地将以往的可拖拽弹出层用 React 实现一遍:
到这里我们已经成功创建了一个弹出层的组件。由于弹出层会随着鼠标的移动而改变它的样式,所以我在 React Element 中添加了行内样式,取值来自自身的状态。
第三步:整合按钮与弹出层组件
剩下的工作就是如何整合两个组件了。我们可以按照以往的方法,给组件定义一些实用的外部接口。
给组件增加外部接口
组件一般都会提供外部接口以方便被调用,从而掩盖其内部实现机制。
先给按钮组件添加两个方法,分别是开和关:
再给弹出层组件做一些修改,增加显示和隐藏的方法:
如何调用子组件接口
上面的两个组件都可以独立完成工作,而且也已经有了十分方便的外部接口供调用。那么整合在一起后,它们之间该如何通信呢?我们可以简单的在创建 React Element 时增加
ref
属性达到目的:这样我们就可以通过对象的
refs
属性获取引用的组件实例。虽然
refs
这种方法简洁明地实现了父组件对子组件的调用,但是难以实现子组件对父组件的通知。所幸我们可以利用使用属性值传递进行父组件与子组件的通信,React 网站首页的示例中就是这么做的。为了说明问题,我们将 popup 多增加一个功能,鼠标点击弹窗层外的区域则关闭弹出层。此时我们的新组件需要将按钮状态重置回
off
状态。简单的做如下修改:上面的代码中通过将新组件的
change
方法通过属性传递给Popup
组件,然后Popup
组件的在确认需要隐藏弹出层后利用change
方法通知父组件。接下来看下父组件的代码:
利用两个子组件合成了
Component
组件后,我利用refs
调用子组件的方法,利用属性传递实现子组件对父组件的通信。而对于外部的调用,则可以通过
ReactDOM.render
方法的返回值得到组件实例。以我们的
Component
组件为例:查看 demo
总结
使用 React 的感觉还是比较方便的,组件之间互相联系起来也很容易。最近看到很多使用 React 制作的组件库,相当值得借鉴学习,同时这也确实推动了我学习 React 的热情。
在以前学习 AngularJS 的时候,也有写过点击出现可拖拽弹出层的示例代码,这也是为什么我这次选用这个例子的原因。两种方案相较,感觉 React 写起来更快一点,这倒不是从技术层面的分析,仅仅是我刚刚熟悉了 React,而 AngularJS 已经好久没用了,不看文档是不会写的了 :stuck_out_tongue:。
Thanks