本文是对React文档:核心概念部分的笔记,内容大致与文档相同。

文档链接

React哲学部分写的很好,务必要看

JSX

JSX是JS的语法扩展,配合react使用,为JS和HTML的混写

JSX支持在大括号({})中书写任何有效JS表达式

同时,JSX也是一个表达式

如下面的例子:

const name="josh perez"
const element=<h1>Hello,{name}</h1>
ReactDOM.render(
element,
document.getElementById("root")
);

在属性中嵌入表达式有两种方法:

//使用引号包裹的字面值
const element =<div tabIndex="0"></div>
//使用花括号包裹的表达式
const element=<img src={user.name}></img>

JSX的属性名称使用小驼峰命名,如class->className

React在渲染已经输入的内容前,会默认进行转义,所有的内容在渲染前已经被转换为字符串,可以有效防止XSS

JSX对象:

Babel将会把JSX转译为一个名为React.createElement()的函数调用

即:

const element=(
<h1 className="greet">
hello
</h1>
);
//与下面的写法相同
const element=React.createElement(
"h1",
{className:"greet"},
"hello"
);
//它事实上创建了一个简化的如下的对象:
const element={
type:"h1",
props:{
className:"greet",
children:"hello"
}
};

它们也被称为React元素,描述了希望在屏幕上看到的内容,React通过读取这些对象来使用他们构建DOM

元素渲染

元素是构成React应用的最小块

React元素是创建开销很小的普通对象。React DOM更新DOM来与React元素保持一致

React 根 DOM 节点

React 根 DOM 节点大概是类似这样的结构:

这个节点内的所有内容都由React DOM管理。
对于根DOM节点,使用ReactDOM.render()进行渲染

const element=<h1>hello</h1>
ReactDOM.render(element,document.getElementById("root"));//root就是根DOM节点的id值

React元素是不可改变对象,一旦被创建就不能修改。更新UI的唯一方法就是创建一个全新的元素。

React 更新策略

React DOM只会进行必要的更新来使DOM达到新的状态。

这种策略只考虑UI在一个指定时间的状态,而不考虑变化的过程,可以减少bug。

组件&props

组件即为代码复用的片段,类似于函数。

组件形式

组件有两种形式:函数和class

//函数组件
function Welcome(props){
return <h1>hello,{props.name}</h1>
}
//ES6-class
class Welcome extends React.Component{
render(){
return(
<div>
hello,{this.props.name}
</div>
);
}
}

渲染组件

React元素可以是DOM标签、或者是用户自己定义的组件

当用户自定义组件,它会将JSX接收道德属性以及子组件转换为单个对象传递给组件,即props

看如下例子:

class Welcome extends React.Component{
render(){
return(
<div>
hello,{this.props.name}
</div>
);
}
}
const elemen=<Welcome name="柳刀" />
ReactDOM.render(
element,
document.getElementById('root')
);

页面会渲染出:hello,柳刀

注意:自定义标签的名称必须为大写,React将会把小写字母开头的组件视为原生DOM组件。

组件的组合与提取

组件可以进行嵌套组合

通常每个新的React应用程序的顶层组件都应该是App组件

对于一些使用程度叫较高的组件,应该进行额外的定义以使组件更容易维护。

props只读

所有的React组件都必须保护它们的props不被更改

state&生命周期

state与props相似,但是可以完全受控与当前的组件。

组件第一次被渲染到DOM中时,被称为“挂载”(mount)

DOM中组件被删除的时候,应该清除计时器,被称为“卸载”(unmount)

见如下的时钟的例子:

class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
//生命周期方法
//在组件已经被渲染到DOM中后运行
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
//在组件即将被卸载时调用
componentWillUnmount() {
clearInterval(this.timerID);
}
//计时器函数,通过这个函数更新state
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);

state

state只能在组件初始化时进行赋默认值

state={}

或者在构造函数中进行构造;

state不能在除了以上两个地方之外使用 this.state.xxx 进行更新

只能使用

this.setState({xxx:xxx}) 进行更新

state的更新可能是异步的

React可能会将多个setState()合并为一个调用,而且props,state也可能异步更新,所以不应该依赖它们的值去决定下一个状态。

如:

//wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
为了解决该问题,可以使用:
// Correct
this.setState((state, props) => ({
counter: state.counter + props.increment
}));

使用参数对state进行更新

state的更新会被合并

为了提高性能,React将setState()合并调用,这里的合并是浅合并,简而言之,就和单独调用他们的效果是一样的。

数据是向下流动的

这被称为"自上而下"或者是"单向"数据流,任何的state总是所属于特定的组件,而从state派生的任何数据只能影响树中低于它们的组件。

这类似与一个瀑布,上层组件在瀑布上层,下层组件在瀑布下层,props是水流,state就是各个组件的水源。

React的数据流动是单向的,也是单层的,即数据不能跨组件流动,但是数据流能够跨组件流动。

事件处理

React事件的命名采用小驼峰而不是纯小写

使用JSX时需要传入一个函数作为事件处理函数而不是一个字符串

//传统
<button onclick="activateLasers()">
Activate Lasers
</button>
//React
<button onClick={activateLasers}>
Activate Lasers
</button>

React中不能通过返回false阻止默认的行为,必须显示地使用preventDefault(取消默认行为)

//传统html
<a href="#" onclick="console.log('The link was clicked.'); return false">
Click me
</a>
//React
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}
return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}

使用class定义组件,事件处理函数通常为一个方法

处理this

class不会默认绑定this,需要手动进行绑定

可以进行:

• 构造函数中绑定this

this.handle=this.handle.bind(this)

• 在调用时绑定this

onClick={this.handle.bind(this)}

• 在声明时使用箭头函数:class fields语法

handle=()=>{}

• 在回调中使用箭头函数

onClick={() => this.handleClick()}

这个方法的问题是在每次渲染组件的时候都会创建不同的回调函数,作为props传入子组件时,这些子组件可能会进行额外的重新渲染,所以不推荐使用。

向事件处理程序传递参数

有两种方法:

<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>

在这两种情况下,React 的事件对象 e 会被作为第二个参数传递。如果通过箭头函数的方式,事件对象必须显式的进行传递,而通过 bind 的方式,事件对象以及更多的参数将会被隐式的进行传递。

条件渲染

元素变量

即通过某个state中的状态进行渲染元素的判断,使用这种方法可以增加代码重用。

&&

例子如下:

<div>
<h1>Hello!</h1>
{unreadMessages.length > 0 &&
<h2>
You have {unreadMessages.length} unread messages.
</h2>
}
</div>

以上例子能够实现是因为:

在JS中,true&&experssion总是返回experssion

false&&experssion总是返回false

三目运算

这里要注意的是,要在可读性和简便性上做好平衡,保持代码的可读性。

如果代码太复杂,应该提取组件。

阻止组件渲染

不希望渲染某组件时,可以直接render null

以下的例子中将不会进行渲染,用这种方法可以在必要的时候阻止特定组件的渲染。

class ClassName extends React.Component{
render(){
return null;
}
}

组件render返回null并不会影响组件的生命周期。

列表&Key

渲染多个组件

在React中渲染多个组件一般使用map方法,从已有数组中产生新数组。

下面的例子进行了解释:

const numbers=[1,2,3,4,5]
const listItems=numbers.map((number)=><li>{number}</li>);
ReactDOM.render(
<ul>{listItems}</ul>,
document.getElementById('root')
);

基础列表组件

规范化,在一个组件中渲染列表:

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

这个例子的作用与前面的例子相同,只不过为每个

  • 分配了一个key

    key

    key的作用是帮助React识别哪些元素发生了改变,所以需要给数组中的每个元素赋予一个特定的值。

    key应该是元素在列表中拥有的独一无二的字符串,通常使用数据中的id作为元素的key。

    如果传入的数据没用id,可以使用元素索引index作为key

    const todoItems = todos.map((todo, index) =>
    // Only do this if items have no stable IDs
    <li key={index}>
    {todo.text}
    </li>
    );

    但是如果列表项目的顺序可能发生变化,那不建议使用索引作为key值,这样会使性能变差,还会引起组件状态问题,如果不显示指定key值,React将默认使用索引作为key值。

    用key提取组件

    元素的key应该放在就近数组的上下文中。

    如例子:

    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()} value={number} />
    );
    return (
    <ul>
    {listItems}
    </ul>
    );
    }

    在上面的例子中,应该在下文,即ListItem处引入数组key,因为它才是数组中的“直接”元素。

    在大多数情况下,在map()方法中的元素设置key属性总是正确的。

    key只是在兄弟节点之间必须唯一

    数组元素只需要在它所在的元素中唯一即可,不需要全局唯一,在生成两个数组的时候,可以使用相同的key值。

    key只会传递消息给React,而不会传递给所在的组件。需要在组件中使用key值时,应该使用其他的属性名称显式传递值。

    在JSX中嵌入map()

    可以将遍历返回的结果通过map()写在{}中,因为JSX允许在大括号嵌入任何表达式。

    如果一个 map() 嵌套了太多层级,那可能就是你提取组件的一个好时机

    表单

    受控组件

    受控组件即为使React成为state的唯一数据源。渲染表单的React组件还控制着用户输入过程中表单发生的操作。

    如下例:

    class NameForm extends React.Component {
    constructor(props) {
    super(props);
    this.state = {value: ''};
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
    }
    handleChange(event) {
    this.setState({value: event.target.value});
    }
    handleSubmit(event) {
    alert('提交的名字: ' + this.state.value);
    event.preventDefault();
    }
    render() {
    return (
    <form onSubmit={this.handleSubmit}>
    <label>
    名字:
    <input type="text" value={this.state.value} onChange={this.handleChange} />
    </label>
    <input type="submit" value="提交" />
    </form>
    );
    }
    }

    在这个例子中,名字表单的值始终为this.state.value,React为state唯一的数据源,handleChange将会在每次案件时执行并更新state。

    对于受控组件,每个state突变都有一个相关的处理函数,这使得修改或者验证用户输入变得更容易。例如可以强制将用户输入大写字母。

    handleChange(event) {
    this.setState({value: event.target.value.toUpperCase()});
    }

    textarea标签

    在html中,textarea通过子元素定义文本。

    在react中,使用value属性代替,使得使用<textarea>的表单和使用单行input表单非常类似。

    select标签

    在react中,select使用value属性

    见例子:

    class FlavorForm extends React.Component {
    constructor(props) {
    super(props);
    this.state = {value: 'coconut'};
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
    }
    handleChange(event) {
    this.setState({value: event.target.value});
    }
    handleSubmit(event) {
    alert('你喜欢的风味是: ' + this.state.value);
    event.preventDefault();
    }
    render() {
    return (
    <form onSubmit={this.handleSubmit}>
    <label>
    选择你喜欢的风味:
    <select value={this.state.value} onChange={this.handleChange}>
    <option value="grapefruit">葡萄柚</option>
    <option value="lime">酸橙</option>
    <option value="coconut">椰子</option>
    <option value="mango">芒果</option>
    </select>
    </label>
    <input type="submit" value="提交" />
    </form>
    );
    }
    }

    以上三个组件都接收一个value属性,可以使用这个属性来实现受控组件。

    可以将数组传入到value属性中,以支持select标签中的多个选项。

    文件input

    在html中,

    <input type="file" />

    的value为只读,所以是一个非受控组件

    处理多个输入

    处理多个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;
    const value = target.name === 'isGoing' ? target.checked : target.value;
    const name = target.name;
    this.setState({
    [name]: value
    });
    }
    render() {
    return (
    <form>
    <label>
    参与:
    <input
    name="isGoing"
    type="checkbox"
    checked={this.state.isGoing}
    onChange={this.handleInputChange} />
    </label>
    <br />
    <label>
    来宾人数:
    <input
    name="numberOfGuests"
    type="number"
    value={this.state.numberOfGuests}
    onChange={this.handleInputChange} />
    </label>
    </form>
    );
    }
    }

    值得注意的是,使用以下语句进行state的更新

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

    这是ES6计算属性名称的语法。

    受控输入空值

    在受控组件上指定value的prop会阻止用户更改输入。如果指定value,输入仍可编辑,可能是意外将value设置为undefined&null

    非受控组件

    状态提升

    多个组件需要反映相同的变化数据,这时建议将共享状态提升到最近的共同父组件中去。

    class Calculator extends React.Component {
    constructor(props) {
    super(props);
    this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
    this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
    this.state = {temperature: '', scale: 'c'};
    }
    handleCelsiusChange(temperature) {
    this.setState({scale: 'c', temperature});
    }
    handleFahrenheitChange(temperature) {
    this.setState({scale: 'f', temperature});
    }
    function tryConvert(temperature, convert) {
    const input = parseFloat(temperature);
    if (Number.isNaN(input)) {
    return '';
    }
    const output = convert(input);
    const rounded = Math.round(output * 1000) / 1000;
    return rounded.toString();
    }
    function toCelsius(fahrenheit) {
    return (fahrenheit - 32) * 5 / 9;
    }
    function toFahrenheit(celsius) {
    return (celsius * 9 / 5) + 32;
    }
    render() {
    const scale = this.state.scale;
    const temperature = this.state.temperature;
    const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
    const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;
    return (
    <div>
    <TemperatureInput
    scale="c"
    temperature={celsius}
    onTemperatureChange={this.handleCelsiusChange} />
    <TemperatureInput
    scale="f"
    temperature={fahrenheit}
    onTemperatureChange={this.handleFahrenheitChange} />
    <BoilingVerdict
    celsius={parseFloat(celsius)} />
    </div>
    );
    }
    }
    class TemperatureInput extends React.Component {
    constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    }
    handleChange(e) {
    this.props.onTemperatureChange(e.target.value);
    }
    render() {
    const temperature = this.props.temperature;
    const scale = this.props.scale;
    return (
    <fieldset>
    <legend>Enter temperature in {scaleNames[scale]}:</legend>
    <input value={temperature}
    onChange={this.handleChange} />
    </fieldset>
    );
    }
    }

    关于状态提升的总结

    在 React 应用中,任何可变数据应当只有一个相对应的唯一“数据源”。通常,state 都是首先添加到需要渲染数据的组件中去。然后,如果其他组件也需要这个 state,那么你可以将它提升至这些组件的最近共同父组件中。你应当依靠自上而下的数据流,而不是尝试在不同组件间同步 state。

    虽然提升 state 方式比双向绑定方式需要编写更多的“样板”代码,但带来的好处是,排查和隔离 bug 所需的工作量将会变少。由于“存在”于组件中的任何 state,仅有组件自己能够修改它,因此 bug 的排查范围被大大缩减了。此外,你也可以使用自定义逻辑来拒绝或转换用户的输入。

    如果某些数据可以由 props 或 state 推导得出,那么它就不应该存在于 state 中。举个例子,本例中我们没有将 celsiusValue 和 fahrenheitValue 一起保存,而是仅保存了最后修改的 temperature 和它的 scale。这是因为另一个输入框的温度值始终可以通过这两个值以及组件的 render() 方法获得。这使得我们能够清除输入框内容,亦或是,在不损失用户操作的输入框内数值精度的前提下对另一个输入框内的转换数值做四舍五入的操作。

    组合VS继承

    React推荐使用组合而不是继承来实现组件间的代码重用

    包含关系

    有些组件在使用前无法得知其子组件的内容,这样的情况可以见例子如下:

    class Part1 extends React.Componment{
    render(){
    return(
    <div>
    {props.children}
    </div>
    );
    }
    }
    class Part2 extends React.Componment{
    render(){
    return(
    <div>
    子组件内容
    </div>
    );
    }
    }

    该结构即在父组件中为子组件留出位置,该位置可以用例子中的 {props.children} 表示,也可以使用

    props.left 或者其他的表达式表示。

    特例关系

    存在某个组件是一个组件的特例的情况,该情况也可以使用组合进行标识

    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}!`);
    }
    }

    在以上例子中,SignUpDialog是Dialog的特例,只要把SignUpDialog作为Dialog的父组件,将数据流向Dialog即可。

    继承

    FaceBook那么多页面都没用到继承,React的组合这么好用了,别用什么继承了。

    注意:组件可以接受任意 props,包括基本数据类型,React 元素以及函数。

    如果需要在组件间复用非UI功能,建议提取为单独的模块,使用import引入。

    [React]核心概念的更多相关文章

    1. [WIP]React 核心概念

      创建: 2019/05/01 Hello World   ReactDOM.render( <p>sample</p>, document.getElementById('ro ...

    2. react 教程—核心概念

      react 核心概念  : https://react.docschina.org/docs/getting-started.html(官网) 或  https://www.w3cschool.cn/ ...

    3. React 核心思想之声明式渲染

      React 发展很快,概念也多,本文目的在于帮助初学者理清 React 核心概念. React 及 React 生态 React 的核心概念只有 2 点: 声明式渲染(Declarative) 基于组 ...

    4. Redux 核心概念

      http://gaearon.github.io/redux/index.html ,文档在 http://rackt.github.io/redux/index.html .本文不是官方文档的翻译. ...

    5. fusionjs 学习二 核心概念

      核心概念 middleware 类似express 的中间件模型(实际上是构建在koa中间件模型上的),但是和koa 的中间件有差异 fusionjs 的中间件同时可以运行在浏览器页面加载的时候 se ...

    6. 深入理解Vue组件3大核心概念

      摘要: 搞懂Vue组件! 作者:浪里行舟 原文:详解vue组件三大核心概念 Fundebug经授权转载,版权归原作者所有. 前言 本文主要介绍属性.事件和插槽这三个vue基础概念.使用方法及其容易被忽 ...

    7. 领域驱动设计(DDD)部分核心概念的个人理解

      领域驱动设计(DDD)是一种基于模型驱动的软件设计方式.它以领域为核心,分析领域中的问题,通过建立一个领域模型来有效的解决领域中的核心的复杂问题.Eric Ivans为领域驱动设计提出了大量的最佳实践 ...

    8. Javascript本质第一篇:核心概念

      很多人在使用Javascript之前都至少使用过C++.C#或Java,面向对象的编程思想已经根深蒂固,恰好Javascript在语法上借鉴了Java,虽然方便了Javascript的入门,但要深入理 ...

    9. [程序设计语言]-[核心概念]-02:名字、作用域和约束(Bindings)

      本系列导航 本系列其他文章目录请戳这里. 1.名字.约束时间(Binding Time) 在本篇博文开始前先介绍两个约定:第一个是“对象”,除非在介绍面向对象语言时,本系列中出现的对象均是指任何可以有 ...

    随机推荐

    1. linux安装国产数据库(金仓数据库,达梦数据库,南大通用数据库)

      今天在公司做的任务是,在Linux的环境下安装三种数据库,结果一种数据库也没有安装好,首先遇到的问题是安装南大通用数据库遇到安装的第五步,就出现问题了,问题是Gbase SDK没有安装成功,以及Gba ...

    2. typescript 02 数据类型

      ---恢复内容开始--- 1.数据类型 ts为了使代码更加规范并利于维护,增加了类型校验 提供了以下几种类型 布尔类型 boolean 数字类型 number 字符串类型 string 数组类型 ar ...

    3. 手机浏览器自动播放视频video(设置autoplay无效)的解决方案

      1.问题的提出 某一天接了个需求,需要在手机的H5页面内加入视频,我开开心心做完,准备交付的时候,问题来了,PM想要用户一进入页面,视频就开始播放,不需要用户手动点击. 2.尝试解决 加autopla ...

    4. jenkins-构建job成功后自动打tag到git仓库

      需求:最近开发同事提出了个要求,每当Jenkins执行上线部署完成后,对当前代码进行自动打TAG到git仓库中,且只有当部署成功后才进行打TAG,防止构建失败也进行打过多的垃圾tag,然后便于下次进行 ...

    5. XCTF---easyjni的WriteUp

      一.题目来源     题目来源:XCTF的mobile区的easyjni题目.     题目下载地址:题目链接地址 二.解题过程     1.下载好题目后,安装到夜神模拟器中,发现有一个输入框和一个按 ...

    6. Linux学习--4.用户和组的管理

      用户和组的管理 前言 本篇文章主要讲Linux系统下用户和组的概念,还有添加用户和组,修改用户和组的基本操作,会涉及不少与之相关的配置文件与命令的介绍,几乎所有 正文 首先,简单提下概念,用户是操作系 ...

    7. C#桌面开发的未来-WebWindow

      WebWindow源码作者博客基于Chromium的Edge体验体验方式一:体验方式二:预期目标:遗留的问题 WebWindow WebWindow是跨平台的库. Web Window的当前实验实现可 ...

    8. Core + Vue 后台管理基础框架1——运行系统

      1.down源码 git clone https://github.com/KINGGUOKUN/SystemManagement.git,项目目录如下: 2.还原数据库 找到项目根目录下System ...

    9. Simulink仿真入门到精通(十六) Simulink基于模型设计的工业应用概述

      16.1 Simulink用途概述 在基于模型设计广泛应用于汽车电子嵌入式开发的今天,MBD(Model Besed Design)技术也逐步推广到各种嵌入式控制方面.与传统的嵌入式开发相比,BMD以 ...

    10. element UI中的tab切换栏

      html代码:(用的是el-tab组件) <el-tabs v-model="activeIndex" type="border-card" @tab-c ...