React文档精读(上篇)

  • pre-notify
  • React中的element是什么
    • 条件渲染
  • element和component
  • 渲染React Element
  • component与pure function
  • 函数式组件和类组件
  • state/状态
    • setState
      • state设置更新时的自动合并机制
      • setState是异步的
  • component和事件
    • 和原生事件的区别
    • this
  • 表单
    • state和受控组件
      • value = {null}
      • 关于select
      • 关于file
    • ref和非受控组件
  • 列表和keys
    • key和重绘
    • li 和
    • key值没法获取
    • 不推荐用索引做为key

pre-notify

previously:JSX,了解一下?javascript

(づ ̄ 3 ̄)づ此文会根据文档的变更而不断更新html

React中的element是什么

上回咱们解释了什么是JSXJSX是React中对Javascript语法的延伸,它容许咱们使用JSX tag来构建一个虚拟DOM做为真实DOM的小型描述文档(它自己是一个Javascript对象)。java

其中,一个JSX tag就是一个React Elementreact

条件渲染

咱们说过JSX是能参与js流程控制的,So咱们能经过if一类的来决定一个React Element何时进行渲染何时不要。es6

let element =  (
    <div>
      {isLogin}&&
      <h2>hello {userName}</h2>
    </div>
  );
复制代码

也支持三元数组

element和component

上面咱们已经知道一个JSX tag就是一个React Element,但一个JSX tag也能够是一个React Componentbash

它长得像这样babel

let elements = <Component10086 />
复制代码

这。。。和以前的有撒区别?dom

首先咱们须要注意一个表明Component/组件的JSX tag必须首字母大写,不然它就会把它当作一个普通的element去渲染。异步

嗯。。。很天然咱们会提出一个疑问,那当作普通的element去渲染和当作一个组件去渲染有什么区别呢?

其实,React中的组件就像是Javascript 中的函数,能够接收任意的输入(咱们称之为props)而且可以返回React elements,这和咱们直接获得React Element的区别在于,咱们能在函数内部再对element进行一次封装,作一些条件渲染,设置state什么的。


So,咱们光建立一个JSX tag是不够的,咱们还须要定义一个对应的函数

function Component10086(props){
  return <h1>Hello Component!</h1>;
}
复制代码

这个函数有一个pros属性,咱们能够像调用函数同样在建立一个JSX tag时传递参数给这个函数,像这样

let elements = <Component10086 prop1='第一个参数' prop2='第二个参数' />

// --- --- ---

//也支持... 来传递多个属性
let data = {
 prop1:'第一个参数'
 ,prop2:'第二个参数'
}
let elements = <Component10086 {...data}/>
复制代码

上面的prop1prop2两个键值对会被封装成一个props对象做为函数的参数传入

function Component10086(props){
  return (
  <h1 className={props.prop2}/*当作属性渲染*/>
  	Hello Component!
    {props.prop1} //当作children渲染
  </h1>;
  )
}
复制代码

[important] 注意:在JSX那一篇中咱们已经说过JSX是javascript语法的延伸,这意味着一个JSX tag能够被赋值给一个js变量,能参与for和if流程控制,能做为函数传参,可以被当作函数返回值。So组件传参时也能将一个JSX tag作为参数进行传递


渲染React Element

咱们已经知道如何构建一个虚拟DOM来描述真实的DOM

let element = <h1 className={class1}>hello React!</h1>
复制代码

(以上通过babel编译后,咱们能够发现它实际上是调用React.createELement来建立一个javascript对象来描述真实dom,上回已详细说过再也不赘述)

[danger] 引入(import)react库时,React必须是首字母大写,由于编译后咱们调用createElement方法时就是首字母大写的React

但怎么将这个虚拟DOM转换成真正的DOM渲染在页面上呢?

这就是咱们React中react-dom库所作的事情了。引入这个库后,咱们能调用其中的render方法将对应的真实DOM渲染到指定的挂载点上。

import ReactDOM from 'react-dom';
...

ReactDOM.render(
    element
    ,document.geElementById('root')
)
复制代码

值得注意的是若是这个React Element被从新渲染,react只会从新渲染这个element中发生改变的dom节点。(咱们说过一个JSX tag能够有children)。


一样的,咱们能使用一样的方式对一个组件进行渲染,

ReactDOM.render(
    <Component10086 prop1='...' />
    ,document.geElementById('root')
)
复制代码

那么,render是怎样区分渲染的是一个组件仍是一个普通的element呢?

咱们调用ReactDOM.render去渲染一个JSX tag时,

咱们首先会查看这个tag的首字母是不是大写,若是是大写就表明是一个component,那么react就会把它当作一个组件去渲染,

它会首先将JSX tag作为参数传递的属性封装成一个props对象,而后再将这个对象传递给组件函数

组件函数接收到参数对象后会进行一系列处理,最终返回处理完成后的React element

最终,render拿到element转换成真正的DOM元素渲染到页面上。

component与pure function

关于React Component,有一点咱们须要注意的是,全部的react组件都必须是一个纯函数。

什么是纯函数呢?

Such functions are called “pure” because they do not attempt to change their inputs, and always return the same result for the same inputs.

React官方给出的便是是,相同的输入会产生相同的输出,而且咱们不能在函数内部更改传递过来的props

这意味着,props是静止的,不可更改的,但一些交互性灰常强的UI组件的一些属性是常常会变化的。

那怎么解决这个问题呢?这就是后话了,后面讲到React state就是用来解决这个问题的。

[important] 注意: 若是属性是不参与render()的那么它就不该该被设计成state

函数式组件和类组件

组件有两种形式,函数式的和类形式的,

//function
function Welcome(props) {
    return <h1>Hello, {props.name}</h1>;
}

//class
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

ReactDOM.render(<Welcome name='ahhh' age='123'/>,document.getElementById('root'));
复制代码

此时这两种写法是等价的,但!

class类的形式支持一些函数式组件不支持的功能,好比说state

state/状态

首先state也是prop,但不一样于普通的props

普通的props咱们能够在JSX tag上传参,react会自动帮咱们将这些参数打包后挂载到组件上(this.props)。

state须要咱们在组件对象上手动设置,

setState

首先咱们在组件中初始化一个组件实例的state

class Clock extends React.Component{
  constructor(props){
    super(props);
    // 在构造函数内部定义初始状态
    this.state = {date:new Date()};
  }
复制代码

若是这个state须要发生改变,咱们须要注意,咱们不能直接经过this.state.date = xxx 这样的形式去改变state,这样是不会触发render()进行重绘的。

咱们须要经过 this.setState 方法(来自继承的React.Component)来改变咱们原有的state

语法: setState(updater[, callback])注意此方法是支持回调的

this.setState({
  date:new Date()
})
复制代码

另外关于setState有两点须要额外注意

state设置更新时的自动合并机制

咱们可能在一个构造函数中初始化多个state键值对

...
this.state = {
    state1:'xx'
    state2:'yy'
}
..
复制代码

React state更新时的自动合并机制容许咱们这样去更新state

this.setState({
    state1:'aaa'
})
复制代码

能够发现咱们只须要在setState中填上咱们要更改的部分而不是整个state对象。

setState是异步的

咱们可能在一个方法中连续使用屡次setState,但因为设置是异步的,咱们不能在第二次调用setState方法时拿到第一次调用setState所设置的值,它们的state都是基于最初的state的。

那么这个问题如何解决呢?

其实setState还有第二种形式,使用回调函数而非对象的形式去更新state,像这样

this.setState((prevState,props)=>({counter:prevState.counter + Math.random()}));
this.setState((prevState,props)=>({counter:prevState.counter + props.increment}))
复制代码

值得一提的是这种写法是 setState(updater[, callback])的语法糖形式,最终仍然会编译成这样执行。

component和事件

和原生事件的区别

  • React事件命名采用的是驼峰式而非原生中的小写形式
  • 原生绑定时传递的是一个字符串,而react则是利用{}
//原生
<button onclick="activateLasers()">
  Activate Lasers
</button>

//React
<button onClick={activateLasers}>
  Activate Lasers
</button>
复制代码
  • react中的ev对象是通过react封装过的
  • 不支持return false
function ActionLink() {
  function handleClick(e) { //这个event不是原生的,而是本身封装的,故不存在兼容问题
    e.preventDefault();
    console.log('The link was clicked.');
  }

  return (
    <a href="#" onClick={handleClick}>
      Click me
    </a>
  );
}
复制代码

this

若是是用类的形式定义的组件,咱们须要注意事件函数中this的指向问题。

在react中咱们不须要手动使用addEventListener对元素进行事件函数的绑定,只须要在JSX tag上像原生行内式绑定事件函数同样注册事件便可。

但这存在一个问题,react并不会帮咱们把回调中的this指向组件实例,甚至这个this也不是指向那个应该绑定的DOM元素

咱们是但愿这个this指向组件实例的,这样咱们能拿到挂载在这个组件实例上的state。而改变回调this指向的方式大概有三种。

  • 经过bind绑定事件处理函数
  • 经过箭头函数包装事件处理函数
  • 经过ES7初始值语法来建立事件处理函数

咱们推荐第三种,十分方便

handleClick = ()=>{
    this.setState(...);
}
复制代码

表单

state和受控组件

正常来讲当咱们在一个input输入后会马上获得相应的显示在界面上。

但React容许咱们在输入后,先把输入的信息hold住,进行一些处理后再显示在界面上。

这是怎么作到的呢?这就是经过咱们以前所说的React state

首先我要让一个input的value等于一个state

<input type="text" onChange={this.handleChange.bind(this,'username')} value={this.state.username}/>
复制代码

咱们知道一个state在react中只能经过调用setState才会触发render致使重绘UI,这意味着只要咱们不马上调用setState,那么input的值就不会马上改变。

在上面的示例中咱们经过给input绑定一个事件,来对输入进行处理

handleChange = (key,event)=>{
    let val = event.target.value;
    this.setState({[key]:val})
}
复制代码

这样咱们就完成了对表单输入的截获,使其受到了咱们的控制,咱们将这样的表单元素称之为受控组件。


有一点要注意咱们在上栗用到了es6的computed property语法

setState({[key]:val})
复制代码

其中{[key]:val}就至关于let o={};o[key]=val,这种语法容许咱们在设置对象的键名时也能使用变量。

咱们之因此要在事件处理函数中传递一个key过去,是由于一张表里可能有不少表单元素,每个都对应咱们组件中的一个state,而这个key就是用来区分他们的。

value = {null}

表示再也不是受控组件

关于select

<select>
  <option value="grapefruit">Grapefruit</option>
  <option value="lime">Lime</option>
  <option selected value="coconut">Coconut</option>
  <option value="mango">Mango</option>
</select>
复制代码

不推荐以上,推荐在select里写value

<select value={this.state.value} onChange={this.handleChange}>
    <option value="grapefruit">Grapefruit</option>
    <option value="lime">Lime</option>
    <option value="coconut">Coconut</option>
    <option value="mango">Mango</option>
</select>
复制代码

多选

<select multiple={true} value={['a', 'b']}>
复制代码

关于file

input=file 是不会受到react控制的

ref和非受控组件

咱们在受控组件中能经过绑定事件处理函数的形式拿到组件中的表单元素。

可是像<input type="file">这种非受控组件,咱们若是还想要截获它的值怎么截获到呢?

首先咱们不可能像其它表单元素同样在它自个儿身上绑定事件处理函数,由于对于file,只有form表单提交时咱们才能拿到file的值,

so咱们只能在form上绑定事件处理函数,那怎么拿到form中的file元素呢?

这就须要利用到React ref

<input type="text" ref="username" />

// 处理函数中
let username = this.refs.username.value
复制代码

以上写法ref=一个字符串已经被废弃

如今推荐这么写

<input type="text" ref={input=>this.username=input} /> //input就是渲染出的真实dom
复制代码

能够发现,这里的ref里对应的是一个函数,此函数会在当此虚拟DOM转成真实DOM并插入到页面以后马上调用,参数接收到的就是插入的真实dom

更新:16.3新特のReact.createRef(),ref={input=>this.username=input}仍可用

列表和keys

记得咱们说过,JSX tag能够做为函数返回值

So,咱们可以这样渲染一个列表

let array = [1,2,3,4,5];
let lists = array.map((item,index)=><li key={index}>{item}</li>);

ReactDOM.render((
  <ul>
    {lists}
  </ul>
), document.getElementById('root'));
复制代码

注意上栗中咱们给每一个li都绑定了一个key,这个key是数组中的索引位置,是独一无二的。

key和重绘

若是咱们不给li绑定key,React会报一个警告,

Warning: Each child in an array or iterator should have a unique "key" prop.

但其实它已经自动帮咱们加上key了。

为何react渲染列表的时须要一个key呢?

记得咱们上面说过react重绘时不会将整个React Element都重绘的吗。

嗯。。。那它是怎么作到的呢?就是利用这个key了,若是没有这个key,它是没法区别element中的tag谁是谁的。

li 和 <ListItem>

有些时候一个li内的内容过于复杂,咱们会将其封装成一个组件

这个时候咱们推荐把key挂载这个li的组件上,而不是组件内部返回的li上

如下示例出资React文档

unction NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// Correct! Key should be specified inside the array.
<ListItem key={number.toString()}
          value={number} />

);
复制代码

key值没法获取

若是咱们在组件上传递了一个key值,这个key值并不会被包装进props对象

不推荐用索引做为key

详见 in-depth explanation on the negative impacts of using an index as a key.


参考:

--- ToBeContinue... ---

相关文章
相关标签/搜索