版本声明

本文使用版本 React v16.2.0

列表 和 键

列表(List), 键(Key)
  回顾一下在javascript中如何转换列表:在数组中使用map()函数对numbers数组中的每个元素依次执操作

const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map((number) => number * 2);
console.log(doubled)  // 2, 4, 6, 8, 10

React 基本借鉴了以上写法,只不过将数组替换成了元素列表

多组件渲染

可以创建元素集合,并用一对大括号 {} 在 JSX 中直接将其引用即可

下面,我们用 JavaScript 的 map() 函数将 numbers 数组循环处理。对于每一项,我们返回一个 <li> 元素。最终,我们将结果元素数组分配给 listItems

const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) => <li>{number}</li>
)

再把整个 listItems 数组包含到一个 <ul> 元素,并渲染到DOM

ReactDOM.render(<ul>{listItems}</ul>,    //这里的{}是JXS的语法格式document.getElementById('root')
)

基本列表组件

通常情况下,我们会在一个组件中渲染列表而不是直接放到root上。重构一下上例

function NumberList(props) {const numbers = props.numbers;const listItems = numbers.map((number) => <li>{number}</li>)return (<ul>{listItems}</ul>)
}const numbers = [1, 2, 3, 4, 5];ReactDOM.render(    //NumberList组件 通过props属性拿到numbers传过来的值,代码如上所示<NumberList numbers={number} />,document.getElementById('root')
)

当运行上述代码的时候,将会受到一个警告:a key should be provided for list items,要求应该为元素提供一个键(注:min版本react无提示)。要去掉这个警告也简单,只需要在listItem的每个li中增加key属性即可,增加后的每个<li>如下

function NumberList(props) {const numbers = props.numbers;const listItems = numbers.map((number) =><li key={number.toString()}>{number}</li>);return (<ul>{listItems}</ul>);
}

当创建元素列表时,“key” 是一个你需要包含的特殊字符串属性,那为什么要包含呢?

键(Keys)

Keys 帮助React标识哪个项被修改、添加或者移除了。数组中的每一个元素都应该有一个唯一不变的键来标识。
 挑选key最好的办法是使用一个在它的同辈元素中不重复的表示字符串。多数情况下可以使用数据中的IDs来作为Keys。但是还是会遇到没有id字段的数据,这种情况你可以使用数据项的索引值

cosnt todoItems = todos.map((todo, index) => // 数据项没有IDs时使用该办法,index是数据项的索引值<li key={index}>{todo.text}</li>
)

如果列表项可能被重新排序,这种用法存在一定的性能问题,React会产生时间复杂度为O(n^3)的算法执行(小疑问:这个时间复杂度为O(n^3)是怎么算的)。因此优先使用数据项本身的字段内容来设置键

使用 Keys 提取组件

Keys只有在数组的上下文中存在意义。例如,如果你提取了一个ListItem组件,应该把key放置在数组处理的<ListItem />元素中,而不能放在ListItem组件自身的<li>根元素上。

以下的用法就是错误的

function ListItem(props) {const value = props.value;return (// 错误!不需要再这里指定 key<li key={value.toString()}> {value}</li>)
}function NumberList(props) {const numbers = props.numbers;const listItems = numbers.map((number) =>// 错误!key 应该在这里指定:<ListItem value={number} />   //这里的<ListItem标签就是调用上面定义的组件 );return (<ul> {listItems} </ul>);
}

应该写成如下

function ListItem(props) {// 正确!这里不需要指定 key :return <li>{props.value}</li>;
}function NumberList(props) {const numbers = props.numbers;const listItems = numbers.map((number) =>// 正确!key 应该在这里被指定<ListItem key={number.toString()}  //这里的<ListItem标签就是调用上面定义的组件 value={number} />);return (<ul>{listItems}</ul>);
}const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(<NumberList numbers={numbers} />,document.getElementById('root')
);

keys 在同辈元素中必须唯一

在数组中使用的 keys 必须在它们的同辈之间唯一。然而它们并不需要全局唯一。我们可以在操作两个不同数组的时候使用相同的 keys :

function Blog(props) {const sidebar = (<ul>{props.posts.map((post) =><li key={post.id}>{post.title}</li>)}</ul>);const content = props.posts.map((post) =><div key={post.id}><h3>{post.title}</h3><p>{post.content}</p></div>);return (<div>{sidebar}<hr />{content}</div>);
}const posts = [{id: 1, title: 'Hello World', content: 'Welcome to learning React!'},{id: 2, title: 'Installation', content: 'You can install React from npm.'}
];
ReactDOM.render(<Blog posts={posts} />,document.getElementById('root')
);

我们能读取props.id,但是读取不了props.key

【注意】键是一个内部映射,他不会作为props传递给组件内部,如果你需要在组件中使用到这个值,可以自定义一个属性名将该值传入到props
要想能够读到props.key,我们可以看下面的代码

const content = posts.map((post) =><Postkey={post.id}id={post.id}title={post.title} />
);

直接在JSX中使用map()

在上例中我们先声明了一个listItem然后在jsx中引用,然而我们也能在JSX中直接引用,称之为 内联map()

function NumberList(props) {const numbers = props.numbers;return (<ul>{ numbers.map((number) =><ListItem key={number.toString()}value={number} />)}</ul>);
}

至于选用哪种风格编写,只要遵循代码清晰易读原则即可

表单

&esmp; HTML中的form表单元素与 React 中的其他 DOM 元素有所不同,因为表单元素自然地保留了一些内部状态。例如,这个纯 HTML 表单接受一个单独的 name:

<form><label>Name:<input type="text" name="name" /></label><input type="submit" value="Submit" />
</form>

该表单和 HTML 表单的默认行为一致,当用户提交此表单时浏览器会打开一个新页面。如果你希望 React 中保持这个行为,也可以工作。但是多数情况下,用一个处理表单提交并访问用户输入到表单中的数据的 JavaScript 函数也很方便。实现这一点的标准方法是使用一种称为“受控组件(controlled components)”的技术。

受控组件(Controlled Components)

在 HTML 中,表单元素如 <input><textarea><select> 表单元素通常保持自己的状态,并根据用户输入进行更新。而在 React 中,可变状态一般保存在组件的 state(状态) 属性中,并且只能通过 setState()更新。
  通过使 React 的 state 成为 “单一数据源原则” 来结合这两个形式。然后渲染表单的 React 组件也可以控制用户输入之后的行为。这种形式,其值由 React 控制的输入表单元素称为“受控组件”。

class NameForm extends React.Component {constructor(props) {super(props);this.state = {value: ''};}handleChange(event) {this.setState({value:event.target.value})}handleSubmit(event) {alert('A name was submitted: ' + this.state.value);event.preventDefault();}render() {return (<form onSubmit={this.handleSubmit.bind(this)}><label>Name:<input type="text" value={this.state.value} onChange={this.handleChange.bind(this} /></label><input type="submit" value="Submit" /></form>)}
}

设置表单元素的value属性之后,其显示值将由this.state.value决定,以满足react状态的同一个数据理念。每次键盘敲击之后会执行handleChange方法以便更新React状态state对象里的数据,显示只也将随着用户的输入而改变。
  由于value属性设置在我们的表单元素上,显示的值总是this.state.value,以满足state 状态的同一数据理念。由于 handleChange 在每次敲击键盘时运行,以更新React state,显示的值将更新为用户的输入
  对于受控组件来说,每一次state的变化都会伴有相关联的处理函数。这使得可以直接修改或验证用户的输入。比如,我们希望强制name的输入都是大写字母,可以如下实现

handleChange(event) {    //强制把value的值变成大写的形式this.setState({value: event.target.value.toUpperCase()});
}

textarea标签

在 HTML 中,<textarea> 元素通过它的子节点定义了它的文本值:

<textarea>Hello there, this is some text in a text area
</textarea>

在 React 中,<textarea> 的赋值使用 value 属性替代。这样一来,表单中 <textarea> 的书写方式接近于单行文本输入框 :

class EssayForm extends React.Component {constructor(props) {super(props);this.state = {value: 'Please write an esay about your favorite DOM element.'}}// ...render() {return (<form  onSubmit={this.handle}><label>Name:<textarea value={this.state.value} onChange={this.handleChange} /></label><input type="submit" value="Submit" /></form>)}
}

注意,this.state.value 在构造函数中初始化,所以这些文本一开始就出现在文本域中。

select 标签

在 HTML 中,<select> 创建了一个下拉列表用法如下

<select><option value="grapefruit">Grapefruit</option><option value="lime">Lime</option>   //下面的selected 代表 已选中<option selected value="coconut">Coconut</option><option value="mango">Mango</option>
<select>

html利用selected默认选中,但在React中,不使用selected,而是给<select>标签中增加一个value属性,这使得受控组件使用更加方便,因为你只需要更新一处变量即可。

React里的代码如下:

class FlavorForm extends React.Component {// ...render() {return (<form onSubmit={this.handleSubmit.bind(this)}><label>Pick your favorite La Croix flavor:<select value={this.state.value} onChange={this.handleChange.bind(this)}><option value="grapefruit">Grapefruit</option><option value="lime">Lime</option><option value="coconut">Coconut</option><option value="mango">Mango</option></select></label><input type="submit" value="Submit" /></form>);}

总的来说,这使 <input type="text"><textarea><select> 都以类似的方式工作 —— 它们都接受一个 value 属性可以用来实现一个受控组件。

多选select

使用多选select时,需要给select标签增加multiple属性,同时给value属性赋值一个数组

<select multiple={true} value={['B', 'C']}>
# 利用e.target合并多个输入元素的处理事件

当您需要处理多个受控的 input 元素时,您可以为每个元素添加一个 name 属性,并且让处理函数根据 event.target.name 的值来选择要做什么。

class Reservation extends React.Component {constructor(props) {super(props);this.state = {isGoing: true,numberOfGuests: 2};this.handleInputChange = this.handleInputChange.bind(this);}handleInputChange(event) {const target = event.target;  //如果是checkbox,那就获取checked值,如果是其他的,那就获取value值const value = target.type === 'checkbox' ? target.checked : target.value;const name = target.name;
//注意这里使用ES6计算的属性名称语法来更新与给定输入名称相对应的 `state`(状态) 键的办法this.setState({ [name]: value });}render() {return (<form><label>Is going:<input name="isGoing" type="checkbox" checked={this.state.isGoing}onChange={this.handleInputChange} /></label><label>Number of guests:<input name="numberOfGuests" type="number" value={this.state.numberOfGuests}onChange={this.handleInputChange} /></label></form>);}
}ReactDOM.render(<Reservation />,document.getElementById('root')
);

注意这里使用ES6计算的属性名称语法来更新与给定输入名称相对应的 state(状态) 键的办法

this.setState({ [name]: value })

由于 setState() 自动将部分状态合并到当前状态,所以我们只需要调用更改的部分即可。

受控 Input 组件的 null 值

关于受控组件与非受控组件,可以参考下面这篇文章:https://www.cnblogs.com/YangqinCao/p/6158117.html
在 受控组件上指定值 prop 可防止用户更改输入,除非您希望如此。 如果你已经指定了一个 value ,但是输入仍然是可编辑的,你可能会意外地把 value 设置为undefined 或 null 。
  以下代码演示了这一点。 (输入首先被锁定,但在短暂的延迟后可以编辑。)

ReactDOM.render(<input value="hi" />, mountNode);setTimeout(function() {  //因为input的value为null的时候,用户是可以编辑的ReactDOM.render(<input value={null} />, mountNode);
}, 1000);

状态提升 (Lifting State Up)

通常情况下,同一个数据的变化需要几个不同的组件来反映。(比如下面的“温度计算器”案例,温度的单位有多种表示形式,比如摄氏度和华氏度,所以同样的数据,我们需要以不同的组件来显示)我们建议提升共享的状态到它们最近的祖先组件中。为了更好的理解,从一个案例来分析

温度计算器

解释:温度的单位有多种表示形式,比如摄氏度和华氏度,所以同样的数据,我们需要以不同的组件来显示
在本案例中,我们采用自下而上的方式来创建一个温度计算器,用来计算在一个给定温度下水是否会沸腾(水温是否高于100C)

(1)创建一个 BoilingVerdict 组件,用来判水是否会沸腾并打印

function Bioling Verdict(props) {if (props.celsius >= 100) {return <p>The water would boil.</p>}return <p>The water would not boil.</p>
}

(2)有了判断温度的组件之后,我们需要一个Calculator组件,他需要包含一个<input>提供我们输入文图,并在this.state.temperature中保存值。另外,以上BoilingVerdict 组件将会获取到该输入值并进行判断

class Caculator extends React.Component {constructor(props) {super(props);this.state = { temperature: '' };    }handleChange(e) {this.setState({ temperature: e.target.value });}render() {const temperature = this.state.temperature;return (<fieldset><legend>Enter temperature in Celsius;</legend><input value={temperature} onChange={this.handleChange.bind(this)} /><BoilingVerdict celsius={parseFloat(templature)} /></fieldset>)}
}

(3)现在我们实现了基础的父子组件通信功能,假设我们有这样的需求:除了一个设施文图的输入之外,还需要有一个华氏温度输入,并且要求两者保持自动同步

我们从Calculator组件中提取出TemperatureInput组件,然后增加新的scale属性(解释:TemperatureInput 组件根据这个scale属性的值来显示不同的单位,如果是c,则显示摄氏度,如果是f,则显示华氏度),值可能是cf

const scaleNames = {c: 'Celsius',f: 'Fahrenheit'
}class TemperatureInput extends React.Component {constructor(props) {super(props);this.state  = { temperature: e.target.value }}handleChange(e) {this.setState({ temperature: e.target.value });}render() {const temperature = this.state.temperature;const scale = this.props.scale;return (<fieldset><legend>Enter temperature in {scaleNames[scale]}</legend><input value={temperature] onChange={this.handleChange} /></fieldset>)}
}

抽出TemperatureInput组件之后,Calculator组件如下

class Calculator extends React.Component {render() {return (<div><TemperatureInput scale="c" /><TemperatureInput scale="f" /></div>);}
}

现在有了两个输入框,但这两个组件是独立存在的,不会互相影响,也就是说,输入其中一个温度另一个并不会改变,与需求不符(解释:我们的需求是一旦改变其中一个温度值,那么另一个以不同单位表示的温度值也要随着变化)
我们不能再Calculator组件中显示BoilingVerdict组件, Calcultor组件不知道当前的温度,因为它是在TemperatureInput组件 中隐藏的, 因此我们需要编写转换函数

(4)编写转换函数
  我们先来实现两个函数在摄氏度和华氏度之间转换

function toCelsius(fahrenheit) {return (fahrenheit - 32) * 5 / 9;
}functin toFahrenheit(celsius0 {return (celsius * 9 / 5) + 32;
}

接下来,编写函数用来接收一个字符串temperature 和一个转化器函数作为参数,并返回一个字符串,这个函数在两个输入之间进行相互转换。为了健壮性,对于无效的temperature值,返回一个空字符串,输出保留三位小数   (提示:其中,convert取值为 toCelsiustoFahrenheit

function tryConvert(temperature, convert) {const input = parseFloat(temperature);if (Number.isNaN(input)) {return '';}const output = convert(input);cosnt rounded = Math.round(output * 1000) / 1000;return rounded.toString();
}

状态提升

目前,两个 TempetureInput 组件都将其值保留在本地状态中,但是我们希望这两个输入是相互同步的。当我们更新摄氏温度输入时,华氏温度输入应该反映并自动更新,反之亦然。

在React 中,共享state(状态) 是通过将其移动到需要的的组件的最接近的共同祖先组件来实现的,这被称之为状态提升(Lifting State Up)。我们将从TemperatureInput组件中移除相关状态本地状态,并将其移动到Calculator组件中
  如果Calculator拥有共享状态,那么他将成为两个输入当前温度的单一数据源。他可以指示他们具有彼此一致的值 。由于两个TemperatureInput组件的props来自于同一个父级Calculator组件,连个输入将始终保持同步

  让我们来一步步实现这个过程
(1)将值挪出组件,用props传入

render() {// const temperature = this.state.temperature;const temperature = this.props.temperature;
}

我们知道,props是只读的,因此我们不能根据子组件调用this.setState()来改变它。这个问题,在React中通常使用 受控的方式来解决。就像DOM<input>一样接收一个valueonChange prop, 所以可以定制Temperature 接受来自其父级组件CalculatortemperatureonTemperatureChange:

thandleChange(e) {// 之前是:this.setState({ temperature: e.target.value });this.props.onTemperatureChange(e.target.value);
}

请注意,之前定义组件中的 templatureonTemperatureChange prop(属性)名称没有特殊的含义。我们可以命名为任何其他名字,就像命名他们为valueonChange。是一个和常见的惯例

onTemperatureChange proptemperature prop 一起由父级的Calculator组件提供,他将通过修改自己的本地state来处理数据变更,从而通过新值重新渲染两个输入。现在我们的代码如下

class TemperatureInput extends React.Component {constructor(props) {super(props);}handleChange(e) {   //从父组件中接收onTemperatureChange()方法,然后在该子组件里面使用,从而可以修改父组件中的state里面的变量的值,从而实现子组件之间的数据管共享(因为他们都是使用的父组件里的数据,然后共享,然后都可以修改父组件里的数据)this.props.onTemperatureChange(e.target.value);}render() {const  temperature = this.props.temperature;const scale = this.props.scale; //从父组件接收传过来的scale属性的值return (<fieldset><legend>Enter temperature in {scalenames[scale]}</legend><input value={tempearature}  onChange={this.handleChange.bind(this)} /></fieldset>)}
}

我们将当前输入的 temperaturescale存储在本地的state中,这是我们输入“提升”的state(状态),他将作为连个输入的“单一数据源”。为了渲染这两个输入,我们需要知道的所有数据的最小表示,如摄氏度输入37,这时Calculator组件状态将是:

{temperature: '37',scale: 'c'
}

我们确实可以存储两个输入框(摄氏度和华氏度)的值,但事实证明是不必要的。我们只要存储最近更改的输入框的值,以及他们所表示的度量衡(scale)就足够了。然后推断出另一个值。这也是我们实现两个输入框保持同步的途径

class Calculator extends React.Component {constructor(props) {super(props);this.state = { temperature: '',  scale: 'c' }}handleCelsiusChange(temperature) {this.setState({ scale: 'c', temperature }); //这里的temperature是一个简写,作用就是把temperature变量的值赋给state里的temperature变量}handleFahrenheitChange(temperature) {this.setState({scale: 'f', temperature })}render() {const scale = this.state.scale;const temperature = this.state.temperature;  //下面的celsius变量和fahrenheit变量是一个计算属性,实时地根据state里的scale变量的值的变化而变化const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;return (<div><TemperatureInput scale="c" temperature={celsuis} onTemperatureChange={this.handleCelsiusChange.bind(this)} /><TemperatureInput scale="f" temperature={fahrenheit}  //这里的fahrenheit是上面定义的fahrenheit变量,是一个计算属性,实时根据state里面的temperature和scale变量的变化而变化onTemperatureChange={this.handleFahrenheitChange.bind(this)} />//把当前父组件里的function传递给子组件使用<BiolingVerdict celsius={parseFloat(celsius)} /></div>)}
}

现在,无论你编辑哪个输入框,Calculator 中的 this.state.temperaturethis.state.scale 都会更新。其中一个输入框获取值,所以任何用户输入都被保留,并且另一个输入总是基于它重新计算值。

让我们回顾一下编辑输入时会发生什么:

  • React 调用在 DOM <input> 上的 onChange 指定的函数。在我们的例子中,这是 TemperatureInput 组件中的 handleChange 方法。
  • TemperatureInput 组件中的handleChange 方法使用 新的期望值 调用 this.props.onTemperatureChange()。TemperatureInput 组件中的props(属性) ,包括 onTemperatureChange,由其父组件 Calculator 提供。
  • 当它预先呈现时, Calculator 指定了代表摄氏度组件的 TemperatureInputonTemperatureChangeCalculatorhandleCelsiusChange 方法,而代表华氏度组件的 TemperatureInputonTemperatureChangeCalculatorhandleFahrenheitChange 方法。因此,会根据我们编辑的输入框,分别调用这两个 Calculator 方法。
  • 在这些方法中, Calculator 组件要求 React 通过使用 新的输入值 和 刚刚编辑的输入框的当前度量衡 来调用 this.setState() 来重新渲染自身
  • React 调用 Calculator 组件的 render 方法来了解 UI 外观应该是什么样子。基于当前温度和激活的度量衡来重新计算两个输入框的值。这里进行温度转换
  • React 使用 Calculator 指定的新 props(属性) 调用各个 TemperatureInput 组件的 render 方法。 它了解 UI 外观应该是什么样子
  • React DOM 更新 DOM 以匹配期望的输入值。我们刚刚编辑的输入框接收当前值,另一个输入框更新为转换后的温度。

状态提升经验总结

解释:所谓的“状态提升”的意思就把子组件里面公共的数据抽取出来,放到父组件里面,然后来公共修改它
在一个 React 应用中,对于任何可变的数据都应该遵循“单一数据源”原则,通常情况下,state首先被添加到需要它进行渲染的组件,然后如果其他的组件也需要它,你可以提升状态到他们最近的祖先组件。你应该依赖从上到下的数据流向,而不是试图在不同的组件中同步状态。

提升状态相对于双向绑定方法需要写更多的"模板"代码,但是有个好处,他可以更方便的找到和隔离bugs。由于任何state(状态)都"存活"若干个组件中,而且可以分别对其独立修改,所以发生错误的可能性大大减少。另外,你可以实现任何定制的逻辑来拒绝或者转换用户输入。

如果某个东西可以从props(属性)或者state(状态)得到,那么他可能不应该存放在state中。例如我们只保存最后编辑的temperaturescale,而不是保存celsiusValuefahrenheitValue解释:因为celsiusValue和fahrenheitValue可以通过temperature和scale来计算得到。所以就不用存储在state中了,避免一切可能的重复存储)。

当你看到UI中的错误,你可以使用React开发者工具来检查props,并向上遍历树,直到找到负责更新状态的组件,这时你可以跟踪到bug的源头:Monitoring State in React DevTools

组合 VS 继承

组合 Composition vs 继承Inheritance

React 拥有一个强大的组合模型,建议使用组合而不是继承以实现代码的重用

接下来同样从案例出发来考虑几个问题,新手一般会用继承,然后这里推荐使用组合

包含

一些组件在设计前无法获知自己要使用什么子组件,尤其是在 SidebarDialog 等通用的 “容器” 中比较常见

这种组件建议使用特殊的children 来直接传递子元素到他们的输出中:

function FancyBorder(props) {return (<div className={'FancyBorder FancyBorder-' + props.color}>// children 表示来自父组件中的子元素{props.children}</div>)
}

这允许其他组件通过嵌套JSX传递任意子组件给他们,比如在父组件中有h1和p子元素

function WelcomeDialog() {return (  //下面使用了<FancyBo9rder>组件,并在标签里面加了h1和p子元素<FancyBo9rder color="blue">    //下面有代码详解<h1 className="Dialog-title">Welcome</h1><p className="Dialog-message">Thank you for your visitiong</p></FancBorder>)
}

讲解: 在 <FancyBorder></FancBorder>之间括起来的任何内容都会被传递到FancyBorder 组件中,作为一个 children prop(属性)。由于 FancyBorder 渲染{props.children} 到一个 <div> 中(解释:可以看上面我们的定义的FancyBorder组件代码部分),传递的元素会呈现在最终的输出中。

这是一种简单的用法,这种案例并不常见,有时候我们需要在一个组件中有多个“占位符”,这种情况下,你可以使用自定义的prop属性,而不是children:

function Contacts() {return <div className="Contacts" />;
}function Chat() {return <div className="Chat" />;
}function SplitPane(props) {return (<div className="SplitPane">     //子组件通过props.left和props.right来接收从父组件里面传过来的left值和right值<div className="SplitPane-left">{props.left}</div><div className="SplitPane-right">{props.right}</div></div>)
}function App() {return (<SplitPane left={       //把上面定义的Contacts 组件当成left变量传给子组件<Contacts />}right={     //把上面定义的Chat 组件当成right变量传给子组件<Chat />}/>)
}

<Contacts /><Chat /> 等 React 元素本质上也是对象,所以可以将其像其他数据一样作为 props(属性) 传递使用。

特例

有时候,我们考虑组件作为其它组件的“特殊情况”。例如,我们可能说一个 WelcomeDialogDialog 的一个特殊用例。
  在React中,也可以使用组合来实现,一个偏“特殊”的组件渲染出一个偏“通用”的组件,通过 props(属性) 配置它:

function FancyBorder(props) {return (<div className={'FancyBorder FancyBorder-' + props.color}>// children 表示来自父组件中的子元素{props.children}</div>);
}function Dialog(props) {return (<FancyBorder color="blue"> //下面有代码讲解<h1 className="Dialog-title"> {props.title} </h1><p className="Dialog-mesage"> {props.message} </p></FancyBorder>)
}function WelcomeDialog() {return (<Dialog title="Welcome" message="Thank you for your visiting">)
}

讲解: 在 <FancyBorder></FancBorder>之间括起来的任何内容都会被传递到FancyBorder 组件中,作为一个 children prop(属性)。由于 FancyBorder 渲染{props.children} 到一个 <div> 中(解释:可以看上面我们的定义的FancyBorder组件代码部分),传递的元素会呈现在最终的输出中。

这对于类定义的组件组合也同样适用

下面的例子是,“父组件向子组件传值,然后子组件再向其子子组件传值

function FancyBorder(props) {return (<div className={'FancyBorder FancyBorder-' + props.color}>{props.children}</div>);
}function Dialog(props) {return (<FancyBorder color="blue"><h1 className="Dialog-title">{props.title}</h1><p className="Dialog-message">{props.message}</p>{props.children}</FancyBorder>);
}class SignUpDialog extends React.Component {constructor(props) {super(props);this.handleChange = this.handleChange.bind(this);this.handleSignUp = this.handleSignUp.bind(this);this.state = {login: ''};}render() {return (<Dialog title="Mars Exploration Program"message="How should we refer to you?"><input value={this.state.login}onChange={this.handleChange} /><button onClick={this.handleSignUp}>Sign Me Up!</button></Dialog>);}handleChange(e) {this.setState({login: e.target.value});}handleSignUp() {alert(`Welcome aboard, ${this.state.login}!`);}
}

如何看待继承?

在 Facebook ,在千万的组件中使用 React,我们还没有发现任何用例,值得我们建议你用继承层次结构来创建组件。

使用 props(属性) 和 组合已经足够灵活来明确、安全的定制一个组件的外观和行为。切记,组件可以接受任意的 props(属性) ,包括原始值、React 元素,或者函数
  如果要在组件之间重用非 U I功能,我们建议将其提取到单独的 JavaScript 模块中。组件可以导入它并使用该函数,对象或类,而不扩展它。

React系统性学习(下)相关推荐

  1. 【杂谈】如何在专家指导下系统性学习自然语言处理

    熟悉有三AI的人应该知道,有三AI在CV领域有春夏秋季划,供大家系统性学习计算机视觉.也有同学问过,咱们NLP怎么没有类似的项目呢! 今天就来了,经过一段时间的准备,这次我们推出了<系统性入门自 ...

  2. react 渲染道具_在React中学习分解道具的基础

    react 渲染道具 by Evelyn Chan 通过伊芙琳·陈 在React中学习分解道具的基础 (Learn the basics of destructuring props in React ...

  3. 了解爬虫的风险与以及如何规避风险-Java网络爬虫系统性学习与实战系列(3)

    了解爬虫的风险与以及如何规避风险-Java网络爬虫系统性学习与实战系列(3) 文章目录 概述 法律风险 民事风险 刑事风险 个人信息的法律风险 著作权的风险(文章.图片.影视等数据) 5不要 3准守 ...

  4. FPGA系统性学习笔记连载_Day7【16位比较器设计】 【原理及verilog实现、仿真】篇

    FPGA系统性学习笔记连载_Day7[16位比较器设计] [原理及verilog实现.仿真]篇 连载<叁芯智能fpga设计与研发-第7天>[16位比较器设计] [原理及verilog实现. ...

  5. React深入学习与源码解析笔记

    ***当前阶段的笔记 *** 「面向实习生阶段」https://www.aliyundrive.com/s/VTME123M4T9 提取码: 8s6v 点击链接保存,或者复制本段内容,打开「阿里云盘」 ...

  6. 【系统性学习】Linux Shell常用命令

    本文重点是脚本开发时会常用的命令和一些提高效率的高频系统操作命令,其他命令(主要是运维方面的,如网络操作.硬件操作.文件打包等)后续补充. 该文配合[系统性学习]Linux Shell易忘重点整理(会 ...

  7. [转载]如何【系统性学习】——从“媒介形态”聊到“DIKW 模型”

    转载自  https://program-think.blogspot.com/2019/10/Systematic-Learning.html 文章目录 ★何为"系统性学习"? ...

  8. java从零开始系统性学习完整超全资源+笔记(还在更新中)

    java从零开始系统性学习完整超全资源+笔记(还在更新中) 前言 资源来自:王汉远java基础, B站视频: https://www.bilibili.com/video/BV1b4411g7bj/? ...

  9. shell脚本的系统性学习笔记

    shell脚本的系统性学习笔记 文章目录 shell脚本的系统性学习笔记 一.入门基础 1.1 脚本的书写格式 1.2脚本的各种执行方式 1.3数据的输入与输出 1.4输入输出重定向 1.5变量 二. ...

最新文章

  1. 电子科大邹权组招聘博后、科研助理和访问学生(生物信息学方向)
  2. Java正則表達式入门
  3. 2021年春季学期-信号与系统-第八次作业参考答案-第六小题
  4. Linux静态IP设置
  5. 一站式SpringBoot for NoSQL Study Tutorial 开发教程学习手册
  6. Linux 下 YUM 安装 PHP 5.5
  7. spring-boot 添加http自动转向https
  8. React Hooks的使用(三)——useRef、useImperativeHandle、useLayoutEffect解析、自定义Hook
  9. java操作redis的操作_Java操作redis简单示例
  10. 别再Prompt了!谷歌提出tuning新方法,强力释放GPT-3潜力!
  11. 力扣1037.有效的回旋镖
  12. labelImg 标签bug
  13. 我的世界如何开直连服务器,我的世界服务器如何连接 连接服务器教程
  14. SandStorm 出品|建设者高光时刻 9 月作品集已上线!
  15. 硬件工程师入门-基础元器件笔记-1-电阻器
  16. 好文推荐(十六)——Git使用详细教程
  17. linux下c使用lzma_使用 LZMA SDK
  18. 组策略学习-统一部署桌面壁纸
  19. 智能雷达感应模组,划定感应区域、屏蔽区域外干扰,智能安防应用
  20. CVPR2022《NFormer: Robust Person Re-identification with Neighbor Transformer》

热门文章

  1. 1144_Docker的基本介绍
  2. python笔试编程题_Python自动化测试笔试面试时常见的编程题
  3. 怎么把几个视频合并成一个?学会这个技巧,轻松掌握
  4. EPICS设备支持的简单示例
  5. promise Eslint sql传参方式 async-await
  6. unity自定义组件
  7. 删除地址栏输入历史记录
  8. 使用leafcutter 做可变剪切分析流程
  9. NGS可变剪切之STAR+rmats软件使用
  10. pytorch笔记:构建LSTM网络,实现训练验证和测试过程