创建和管理React组件的各种方式,涌现的大量状态管理工具等等都是这些挑战的焦点。我们今天能做的就是在React(基于社区选择)中将最常用的做法引入桌面并讨论它们。
其中,我们将学习React中的一些有用的主题和术语。这些主题包括:
目录
常规的Component
Props 验证
Component之间的交互
初始化Component
ES6+ Components (与正常组件有所不同)
无状态组件
这篇文章是很基础的,所以你可能会发现自己正在阅读你已经知道的东西。如果您正在寻找更高级的主题,您可以阅读我们的其它关于React的文章。
常规的 React Component
按照常规,我的意思是常见的,你可能在大多数代码库和文章中看到过:
var Hello = React.createClass({ render: function() { return <div>Hello {this.props.name}</div>; } }); ReactDOM.render( <Hello name="World" />, document.getElementById('container') );
请记住,常规并不意味着最佳实践。 React官方文档推广它的原因,只是因为它很常见。
React.createClass函数必须在Object类型的参数中传递。这个对象定义了一个react组件。 render属性是必需的,也是最重要的属性。它负责解析JavaScript,JSX中的HTML。
Web应用程序只有在动态时才有意思。任何UI库都会提供一种方法来传递系统的数据,React的想法是通过props object来传递数据。所以当你看到下面的JSX时:
<h1>My name is {name}</h1>
我们告诉React,当组件被调用时,带有值的name属性应该像上面例子中的render一样传递:
<Hello name="World" />
渲染方法需要一个组件输出和DOM输出。这是一个React组件的基本解剖。
相关课程:React入门
States & Props
动态应用程序必须将数据传递给系统。在React中,数据移动主要发生在提供原始数据的组件和外部服务之间(例如HTTP,localStorage)。
Props是不可改变的,这意味着它们只能从父组件传递下来,不能改变。这提出了一个挑战,因为现代web程序不可能在页面加载的时候就准备好所有的数据。 Ajax或其它事件也有会可能发生都有可能引发数据的变更,当数据返回时,数据就需要更新。这就是引入React状态地方。
在初始化React时,我们定义一个初始状态并保持state与props同步。一旦state更新,props可以很容易地保持同步:
var Counter = React.createClass({ getInitialState: function() { return { counter: 0 }; }, render: function() { return <div> <h2>{this.props.name}</h2> {this.state.counter} </div>; } }); ReactDOM.render( <Counter name={'Counter'} />, document.getElementById('container') );
父组件
这非常直截了当。如果组件在其render中渲染另一个组件,则渲染器是渲染的所有者(父级)。渲染器拥有渲染的组件并控制它。
我们来看看另一个例子:
var CounterDisplay = React.createClass({ render: function(){ return <div>{this.props.counterProp}</div> } })var Counter = React.createClass({ getInitialState: function() { return { counter: 0 }; }, render: function() { // Child component rendered // React will throw an error if the the DOM doesn't have a single parent return <div> <h2>{this.props.name}</h2> <CounterDisplay counterProp={this.state.counter}></CounterDisplay> </div>; } }); ReactDOM.render( <Counter name={'Counter'} />, document.getElementById('container') );
Counter现在呈现另一个组件CounterDisplay。 Counter负责管理和同步CounterDisplay的props。您可以看到我们如何通过props将state传递给子组件。这些props也可以像state一样命名为计数器,但这可能会让初学者感到困惑,所以我给了它一个不同的名字。
组件交互
如果我们在子组件中有一个按钮(或更多)来增加或减少在父组件中管理的整数(状态),该怎么办?我们做什么?
React组件交互有两种形式:从父到子组件的数据流以及从子到父的数据流。我们已经看到了如何通过使用props实现父组件对子组件的数据流动。
为了在React实现子组件对父组件之间的数据传递,我们使用通过父组件传递给子组件的处理程序作为props, 父组件知道这样的活动可能发生在子组件身上,因此它为发生变化时设置了一个处理程序。更像事件:
// 子组件var CounterDisplay = React.createClass({ render: function(){ // Calls the handler props once events are fired return <div> <div>{this.props.counterProp}</div> <button onClick={this.props.incrementCounter}>+</button> <button onClick={this.props.decrementCounter}>-</button> </div> } })
// 父组件var Counter = React.createClass({ getInitialState: function() { return { counter: 0 }; }, handleIncrement(){ // Update counter state this.setState({counter : this.state.counter+1}); }, handleDecrement(){ // Update counter state this.setState({counter : this.state.counter-1}); }, render: function() { // Pass down handlers to CounterDisplay component return <div> <h2>{this.props.name}</h2> <CounterDisplay counterProp={this.state.counter} incrementCounter={this.handleIncrement} decrementCounter={this.handleDecrement}></CounterDisplay> </div>; } }); ReactDOM.render( <Counter name={'Counter'} />, document.getElementById('container') );
CounterDisplay 组件点击的时候,他们的处理function不在该组件的任何位置,相反,处理程序由父组件Counter处理。该处理程序反过来使用this.setState()方法更新状态,并且计数器显示(子组件)props得到更新
初始化Component
不仅state有能力使用getInitialState方法重新设置初始值。如果需要,您还可以为将在组件负载上使用的props设置默认值。为了达到这个目的,你可以使用getDefaultProps方法:
getDefaultProps: function() { return { name: 'Counter' }; },
这对于在应用程序中设置默认值非常有用。
Prop 验证
关于React组件的一个好消息是,我看到开发人员喜欢并且强调它的可用性。您可以将任何组件放在应用程序中,只要您遵守它的规则,就可以执行它旨在执行的操作。当制作我自己的可重用组件时,我该如何制定自己的规则?Props验证是你的问题的答案。
按名称进行验证有助于您确信流入组件的数据按照您期望的组织结构进行组织。用户不能将你设置为数组的数据,用字符串传进来。这就是用法
var CounterDisplay = React.createClass({ render: function(){ // Calls the handler props once events are fired return <div> <div>{this.props.counterProp}</div> <br /> <button onClick={this.props.incrementCounter}>+</button> <button onClick={this.props.decrementCounter}>-</button> </div> }, // Setup validation for each props propTypes: { // Must be a number counterProp: React.PropTypes.number.isRequired, // Must be functions incrementCounter: React.PropTypes.func.isRequired, decrementCounter: React.PropTypes.func.isRequired } })
如果您所需要的只是验证类型,而不是它是否存在,您可以忽略isRequired:
propTypes: { // Should be a number counterProp: React.PropTypes.number, // Should be functions incrementCounter: React.PropTypes.func, decrementCounter: React.PropTypes.func }
Class Component (ES6)
React.createClass并不是创建有效React 组件的唯一可能方法。使用ES6(这真的很酷),我们可以使用clsaa来创建react组件。
// Extends React.Compoentclass Comment extends React.Component { // Render method now a class member rather than // object property render(){ return <h1>{this.props.name}</h1>; } } React.render(<Comment name={'Comment'}/>, document.getElementById('container'));
组件的名称是类名,class继承React.Component的功能。
在class里面设置state
如果你使用ES6的方法来构造组件,那么设置state的方法也会有一些改变;
class Comment extends React.Component { constructor(props) { super(props); this.state = { counter: 0 }; } render(){ return <h1>{this.props.name}</h1>; } } React.render(<Comment name={'Comment'}/>, document.getElementById('container'));
初始状态现在在class构造函数中设置,而不是使用getInitialState。
在class中初始和验证props
// Validation Comment.propTypes = { counterProp: React.PropTypes.number.isRequired, incrementCounter: React.PropTypes.func.isRequired, decrementCounter: React.PropTypes.func.isRequired}; // Defaults Comment.defaultProps = { name: 'Counter' };
还有一些细微的差异,但以上是你应该注意的。你可以阅读关于差异的文章 。
无状态组件
当组件没有处理任何状态时,那么您可以使用这些功能:
function CommentDisplay(props) { return <div> <div>{props.counterProp}</div> <br /> <button onClick={props.incrementCounter}>+</button> <button onClick={props.decrementCounter}>-</button> </div>}
就是这么简单!