React最佳实践
2017-03-01 08:55
357 查看
这篇文章主要讲的是在React组件开发中的一些规范,我也将其实践于项目中,结合我在项目中的实践进行了一些批注,意欲解释其为什么要这么做,希望能给新开发react的小伙伴有一些帮助。
渣翻见谅…
概述
组件的组织结构
基础组件结构
规范props格式
推荐模式
Props数据的处理
State数据的处理
在render方法中使用三元表达式
View层组件
容器组件
反模式
条件判断语句
不要在 render 方法中缓存数据
存在性检验
根据props来设置state
实例
合理命名事件处理函数
对event合理命名
使用propType
使用实体字符
坑
表格
工具库
classNames库
其他
JSX
ES2015
react-rails
rails-assets
flux
本文讨论我们如何正确的在Rails框架中编写 React.js 。我们一直在努力用最优雅的方式去编写React组件,在多次的失败尝试之后,我们得出了以下这些所推荐的方式;如果有的地方看起来不太适合,那么他可能真的不合适。希望你能让我们也知道哪些地方不合适。
所有的例子基于ES2015语法编写,使用 babel 转译以及采用 react-rails
gem 框架。
class的定义
constructor
事件绑定
组件生命周期事件
get/set 数据处理函数
render 方法
defaultProps
proptypes
译注:
在willMount 生命周期中virtualDom已经完成,所以可以进行事件绑定
在DidMount 中组件已经被渲染,此时可以用this.refs了,写canvas的同学要注意这点。
两个及以上props换行书写。
使用 Get/Set访问器属性 来做数据处理。
参阅:4.2 反模式-不要在 render 方法中缓存数据
数据处理函数的命名应该在开头处添加一个动词,以增加可读性。
这里的对象方法 必须 返回一个 布尔值 。
译注:
这里想表达的是,state做为管理组件状态的数据,数据结构应该尽量简单,清晰的表示组件状态,复杂的业务逻辑应该放在父组件通过props传递的方式来获得,所以作者推荐state的处理必须返回布尔值。
参阅: 4.1 反模式-条件判断语句
在 render 中使用三元表达式来完成渲染判断。
以view层为基本最小单元来组织组件。不要创建只能一次性使用的巨无霸组件以及域组件 。
使用一个父级容器来进行数据请求,然后用子级组件来进行对应的渲染 — Jason Bonta
Bad
Good
了解更多
相关视频
不要在 render 方法中写判断语句,多采用三元或短路。
这个情况最好的方法就是使用 父组件容器 来管理state,新的state数据作为props传给子组件。
参阅: 2.2推荐模式-State数据的处理
不要在 render 方法中进行数据处理、state/props的保存。
我认为这样的风格是为了性能考量。
参阅:2.1推荐模式-props数据的处理
不要在根组件检查存在性。
使用函数式编写组件时,返回的数据类型应该恒定。
所有组件都 必须 被渲染。
所以可以增加一个自己觉得合适的默认值—— defaultProps 。
译注:
所有组件都进行渲染主要是为了方便进行控制和管理。
即不需要呈现的组件,渲染为一个空标签即可。
如果一个组件通过条件判断来决定渲染与否,那么可以将存在性检验写在其私有的组件语句中。
上述的存在性检验主要用于对象或者数组。
组件所需要的props中,嵌套的数据类型使用 PropTypes来进行检测。
根据props设置state,并且给予props明确的含义。
了解更多: Props
in getInitialState Is an Anti-Pattern
为了让你给事件处理函数起个有意义的名字,先写事件触发的业务逻辑,之后再进行事件命名。
处理函数的命名应该:
– 以 handle 作为开始
– 以其对应的事件作为名字的结束 (例如, Click , Change )
– 使用一般现在时
如果你需要一些消除歧义的名称,你可以在 handle 和事件名中间增加一些补充信息,
比如说你想要区分 onChange 事件,就可以是: handleNameChange 和 handleAgeChange。不过若真如此,你可能需要想一下是不是需要创造一个新的组件。
对于所有者自身的事件使用自定义事件来命名。
proptype表示组件所期望的数据类型,以及用于产生有意义的警告。
如果接受到的 name 字段而不是 string , MyValidatedComponent 会打出一个警告。
这个组件要求 所传入的 props 必须有 name 字段。
组件会对要求字段进行验证。
了解更多: Prop
Validation
使用React原生的 String.fromCharCode() 来处理特殊字符。
了解更多: JSX
Gotchas
在
如果你忘记了
使用 classNames 这个库来做类名判断与管理。
了解更多: Class
Name Manipulation
我在的项目组中曾经有一些 CoffeeScript的拥趸,不幸的事情是在写CoffeeScript数据模板的时候,因为JSX进行了抽象导致模板钩子的实现改变了。
所以我们不推荐使用CoffeeScript来编写render方法。
当必须使用CoffeeScript时,你可以看一下我们怎么使用CoffeeScript: CoffeeScript
and JSX
react-rails 现在已经集成了 babel ,所有babel能做的事情,在Rails中也可以,你可以参阅文档来了解其相关配置。
react-rails 可以用于所有使用React开发的Rails
Apps。它使得Rails与React之间可以完美结合。
rails-assets 是一个资源管理框架,用于管理项目中的js/css等静态资源。我这里最流行的React库是 Bower 它其可以非常方便的添加依赖与react资源。
警告:rails-assets 需要 Sprockets来进行 bower项目的访问。这是rails使用js传统的方法的胜利,这种方法不需要借助js模块化管理工具。
使用 Alt 来进行flux开发。flux
文档提到建议配合Alt开发。
原文地址: react-patterns
渣翻见谅…
React最佳实践
目录
概述组件的组织结构
基础组件结构
规范props格式
推荐模式
Props数据的处理
State数据的处理
在render方法中使用三元表达式
View层组件
容器组件
反模式
条件判断语句
不要在 render 方法中缓存数据
存在性检验
根据props来设置state
实例
合理命名事件处理函数
对event合理命名
使用propType
使用实体字符
坑
表格
工具库
classNames库
其他
JSX
ES2015
react-rails
rails-assets
flux
概述
本文讨论我们如何正确的在Rails框架中编写 React.js 。我们一直在努力用最优雅的方式去编写React组件,在多次的失败尝试之后,我们得出了以下这些所推荐的方式;如果有的地方看起来不太适合,那么他可能真的不合适。希望你能让我们也知道哪些地方不合适。所有的例子基于ES2015语法编写,使用 babel 转译以及采用 react-rails
gem 框架。
组件的组织结构
基础组件结构
class的定义constructor
事件绑定
组件生命周期事件
get/set 数据处理函数
render 方法
defaultProps
proptypes
class Personextends React.Component { constructor (props) { super(props); this.state = { smiling: false }; this.handleClick = () => { this.setState({smiling: !this.state.smiling}); }; } componentWillMount () { // 事件监听 (flux/reduce 的Store、WebSocket、document等) add event listeners (Flux Store, WebSocket, document, etc.) } componentDidMount () { // React.getDOMNode() DOM操作等相关逻辑 } componentWillUnmount () { // 移除事件监听 remove event listeners (Flux Store, WebSocket, document, etc.) } getsmilingMessage () { return (this.state.smiling) ? "is smiling" : ""; } render () { return ( <divonClick={this.handleClick}> {this.props.name} {this.smilingMessage} </div> ); } } Person.defaultProps = { name: 'Guest' }; Person.propTypes = { name: React.PropTypes.string };
译注:
在willMount 生命周期中virtualDom已经完成,所以可以进行事件绑定
在DidMount 中组件已经被渲染,此时可以用this.refs了,写canvas的同学要注意这点。
规范Props格式
两个及以上props换行书写。// bad <Person firstName="Michael" /> // good <PersonfirstName="Michael" /> // bad <PersonfirstName="Michael" lastName="Chan" occupation="Designer" favoriteFood="Drunken Noodles" /> // good <Person firstName="Michael" lastName="Chan" occupation="Designer" favoriteFood="Drunken Noodles" />
推荐模式
Props数据的处理
使用 Get/Set访问器属性 来做数据处理。// bad firstAndLastName () { return `${this.props.firstName} ${this.props.lastname}`; } // good getfullName () { return `${this.props.firstName} ${this.props.lastname}`; }
参阅:4.2 反模式-不要在 render 方法中缓存数据
State数据的处理
数据处理函数的命名应该在开头处添加一个动词,以增加可读性。// bad happyAndKnowsIt () { return this.state.happy && this.state.knowsIt; }
// good getisHappyAndKnowsIt () { return this.state.happy && this.state.knowsIt; }
这里的对象方法 必须 返回一个 布尔值 。
译注:
这里想表达的是,state做为管理组件状态的数据,数据结构应该尽量简单,清晰的表示组件状态,复杂的业务逻辑应该放在父组件通过props传递的方式来获得,所以作者推荐state的处理必须返回布尔值。
参阅: 4.1 反模式-条件判断语句
在render方法中使用三元表达式
在 render 中使用三元表达式来完成渲染判断。// bad renderSmilingStatement () { return <strong>{(this.state.isSmiling) ? " is smiling." : ""}</strong>; }, render () { return <div>{this.props.name}{this.renderSmilingStatement()}</div>; }
// good render () { return ( <div> {this.props.name} {(this.state.smiling) ? <span>is smiling</span> : null } </div> ); }
View层组件
以view层为基本最小单元来组织组件。不要创建只能一次性使用的巨无霸组件以及域组件 。// bad class PeopleWrappedInBSRowextends React.Component { render () { return ( <divclassName="row"> <Peoplepeople={this.state.people} /> </div> ); } }
// good class BSRowextends React.Component { render () { return <divclassName="row">{this.props.children}</div>; } } class SomeViewextends React.Component { render () { return ( <BSRow> <Peoplepeople={this.state.people} /> </BSRow> ); } }
容器组件
使用一个父级容器来进行数据请求,然后用子级组件来进行对应的渲染 — Jason BontaBad
// CommentList.js class CommentListextends React.Component { getInitialState () { return { comments: [] }; } componentDidMount () { $.ajax({ url: "/my-comments.json", dataType: 'json', success: function(comments) { this.setState({comments: comments}); }.bind(this) }); } render () { return ( <ul> {this.state.comments.map(({body, author}) => { return <li>{body}—{author}</li>; })} </ul> ); } }
Good
// CommentList.js class CommentListextends React.Component { render() { return ( <ul> {this.props.comments.map(({body, author}) => { return <li>{body}—{author}</li>; })} </ul> ); } }
// CommentListContainer.js class CommentListContainerextends React.Component { getInitialState () { return { comments: [] } } componentDidMount () { $.ajax({ url: "/my-comments.json", dataType: 'json', success: function(comments) { this.setState({comments: comments}); }.bind(this) }); } render () { return <CommentListcomments={this.state.comments} />; } }
了解更多
相关视频
反模式
条件判断语句
不要在 render 方法中写判断语句,多采用三元或短路。// bad render () { return <div>{if (this.state.happy && this.state.knowsIt) { return "Clapping hands" }</div>; }
// better getisTotesHappy() { return this.state.happy && this.state.knowsIt; }, render() { return <div>{(this.isTotesHappy) && "Clapping hands"}</div>; }
这个情况最好的方法就是使用 父组件容器 来管理state,新的state数据作为props传给子组件。
参阅: 2.2推荐模式-State数据的处理
不要在render方法中缓存数据
不要在 render 方法中进行数据处理、state/props的保存。// bad render () { letname = `Mrs. ${this.props.name}`; return <div>{name}</div>; } // good render () { return <div>{`Mrs. ${this.props.name}`}</div>; }
// best getfancyName () { return `Mrs. ${this.props.name}`; } render () { return <div>{this.fancyName}</div>; }
我认为这样的风格是为了性能考量。
参阅:2.1推荐模式-props数据的处理
存在性检验
不要在根组件检查存在性。使用函数式编写组件时,返回的数据类型应该恒定。
// bad const Person = props => { if (this.props.firstName) return <div>{this.props.firstName}</div> else return null }
所有组件都 必须 被渲染。
所以可以增加一个自己觉得合适的默认值—— defaultProps 。
译注:
所有组件都进行渲染主要是为了方便进行控制和管理。
即不需要呈现的组件,渲染为一个空标签即可。
// better const Person = props => <div>{this.props.firstName}</div> Person.defaultProps = { firstName: "Guest" }
如果一个组件通过条件判断来决定渲染与否,那么可以将存在性检验写在其私有的组件语句中。
// best const TheOwnerComponent = props => <div> {props.person && <Person {...props.person} />} </div>
上述的存在性检验主要用于对象或者数组。
组件所需要的props中,嵌套的数据类型使用 PropTypes来进行检测。
根据props来设置state
根据props设置state,并且给予props明确的含义。// bad getInitialState () { return { items: this.props.items }; }
// good getInitialState () { return { items: this.props.initialItems }; }
了解更多: Props
in getInitialState Is an Anti-Pattern
实例
合理命名事件处理函数
为了让你给事件处理函数起个有意义的名字,先写事件触发的业务逻辑,之后再进行事件命名。// bad punchABadger () { /*...*/ }, render () { return <divonClick={this.punchABadger} />; }
// good handleClick () { /*...*/ }, render () { return <divonClick={this.handleClick} />; }
处理函数的命名应该:
– 以 handle 作为开始
– 以其对应的事件作为名字的结束 (例如, Click , Change )
– 使用一般现在时
如果你需要一些消除歧义的名称,你可以在 handle 和事件名中间增加一些补充信息,
比如说你想要区分 onChange 事件,就可以是: handleNameChange 和 handleAgeChange。不过若真如此,你可能需要想一下是不是需要创造一个新的组件。
对event合理命名
对于所有者自身的事件使用自定义事件来命名。class Ownerextends React.Component { handleDelete () { // handle Ownee's onDelete event } render () { return <OwneeonDelete={this.handleDelete} />; } } class Owneeextends React.Component { render () { return <divonChange={this.props.onDelete} />; } } Ownee.propTypes = { onDelete: React.PropTypes.func.isRequired };
使用propType
proptype表示组件所期望的数据类型,以及用于产生有意义的警告。MyValidatedComponent.propTypes = { name: React.PropTypes.string };
如果接受到的 name 字段而不是 string , MyValidatedComponent 会打出一个警告。
<Personname=1337 /> // Warning: Invalid prop `name` of type `number` supplied to `MyValidatedComponent`, expected `string`.
这个组件要求 所传入的 props 必须有 name 字段。
MyValidatedComponent.propTypes = { name: React.PropTypes.string.isRequired }
组件会对要求字段进行验证。
<Person /> // Warning: Required prop `name` was not specified in `Person`
了解更多: Prop
Validation
使用实体字符
使用React原生的 String.fromCharCode() 来处理特殊字符。// bad <div>PiCO · Mascot</div> // nope <div>PiCO · Mascot</div> // good <div>{'PiCO ' + String.fromCharCode(183) + ' Mascot'}</div> // better <div>{`PiCO ${String.fromCharCode(183)} Mascot`}</div>
了解更多: JSX
Gotchas
坑
表格
在 table标签中要记得使用
tbody。
如果你忘记了
tbody标签,虽然React不会像浏览器那样觉得你很ZZ然后帮你加一个,但是React会继续渲染JSX即给
table里面直接插入
tr,最后的结果可能不可控,让你一脸懵逼,所以记得使用
tbody。
// bad render () { return ( <table> <tr>...</tr> </table> ); } // good render () { return ( <table> <tbody> <tr>...</tr> </tbody> </table> ); }
工具库
classNames库
使用 classNames 这个库来做类名判断与管理。// bad getclasses () { letclasses = ['MyComponent']; if (this.state.active) { classes.push('MyComponent--active'); } return classes.join(' '); } render () { return <divclassName={this.classes} />; }
// good render () { letclasses = { 'MyComponent': true, 'MyComponent--active': this.state.active }; return <divclassName={classnames(classes)} />; }
了解更多: Class
Name Manipulation
其他
JSX
我在的项目组中曾经有一些 CoffeeScript的拥趸,不幸的事情是在写CoffeeScript数据模板的时候,因为JSX进行了抽象导致模板钩子的实现改变了。所以我们不推荐使用CoffeeScript来编写render方法。
当必须使用CoffeeScript时,你可以看一下我们怎么使用CoffeeScript: CoffeeScript
and JSX
ES2015
react-rails 现在已经集成了 babel ,所有babel能做的事情,在Rails中也可以,你可以参阅文档来了解其相关配置。
react-rails
react-rails 可以用于所有使用React开发的RailsApps。它使得Rails与React之间可以完美结合。
rails-assets
rails-assets 是一个资源管理框架,用于管理项目中的js/css等静态资源。我这里最流行的React库是 Bower 它其可以非常方便的添加依赖与react资源。警告:rails-assets 需要 Sprockets来进行 bower项目的访问。这是rails使用js传统的方法的胜利,这种方法不需要借助js模块化管理工具。
flux
使用 Alt 来进行flux开发。flux文档提到建议配合Alt开发。
原文地址: react-patterns
相关文章推荐
- React.js 2016 最佳实践 徬梓阅读 1584收藏 71
- 我们编写 React 组件的最佳实践
- 将React组件迁移到ES6最佳实践
- React Native 如何设计登录模块?有何最佳实践?
- Redux进阶系列1: React+Redux项目结构最佳实践
- 总结 React 组件的三种写法 及最佳实践 [涨经验]
- React 最佳实践——那些 React 没告诉你但很重要的事
- 总结 React 组件的三种写法 及最佳实践
- React-Native学习笔记——react-redux最佳实践应用篇
- 编写React组件的最佳实践
- [转] React 最佳实践——那些 React 没告诉你但很重要的事
- react最佳入门实践(1)
- React服务器渲染最佳实践
- React 最佳实践——那些 React 没告诉你但很重要的事
- React + Redux 最佳实践 学习之 redux
- React系列之开发大型网站最佳实践
- React 最佳实践——那些 React 没告诉你但很重要的事
- react、react-router、redux 也许是最佳小实践1
- 【推荐】react 最佳实践
- React + Redux 最佳实践 学习之 路由