React组件基础
Posted webchang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React组件基础相关的知识,希望对你有一定的参考价值。
目录
1. 模块化和组件化的区别
- 模块:向外提供特定功能的js程序, 一般就是一个js文件,达到复用代码的目的。当应用的js都以模块来编写的, 这个应用就是一个模块化的应用。
- 组件:用来实现局部功能效果的代码和资源的集合(html/css/js/image等等),组件是一个资源的集合,不仅仅包括js文件。当应用是以多组件的方式实现, 这个应用就是一个组件化的应用。
2. react组件介绍
JSX语法中的标签如果以小写字母开头,会被react默认按照html标签来解析,如果html中没有该标签对应的同名元素则报错。
如果以大写字母开头,react就会去渲染对应的组件,如果组件没有定义,则报错。
组件是react的一等公民,使用react就是在使用组件,组件表示页面的部分功能,组合多个组件实现完整页面功能。
3. react组件的两种创建方式
3.1 函数组件
使用JS的函数(或箭头函数)创建的组件叫做函数组件。
- 函数名称必须以大写字母开头。react以此来区分组件和普通的react元素。使用函数名作为组件标签名
- 函数组件必须有返回值,表示该组件的结构
- 如果返回值是null,表示不渲染任何内容
// 函数组件
function Hello()
console.log(this) // undefined
return (
<h1>hello</h1>
)
// 渲染
// ReactDOM.render(<Hello />, document.getElementById('root'))
// 使用单标签和双标签都可以
ReactDOM.render(<Hello></Hello>, document.getElementById('root'))
// 使用箭头函数创建组件
const Hello = () => <div>函数组件哦</div>;
如果直接打印this,结果是undefined。原因:在严格模式下,
- 禁止this指向全局对象
- 顶层的this指向undefined
babel在转换jsx语法时,会启用严格模式,从图中可以看书jsx语法是React.createElement的语法糖
当执行ReactDOM.render(<Hello></Hello>, document.getElementById('root'))
时发生了什么?
- react解析组件标签,找到对应的组件
- 发现组件是函数组件,则调用该函数,将返回的虚拟DOM转为真实DOM,然后渲染到页面上
3.2 使用类创建组件
使用ES6的class创建的组件叫做类组件。
- 类名必须以大写字母开头
- 类组件应该继承React.Component父类,从而可以使用父类中的属性和方法
- 类组件必须提供render()方法
- render()方法必须有返回值,表示组件的结构。如果不想渲染任何内容,也要返回null
// 类组件
class Hello extends React.Component
render()
return (
<div>这是类组件</div>
)
// 渲染
ReactDOM.render(<Hello/>, document.getElementById('root'))
- React解析组件标签,找到了 Component组件。
- 发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的 render方法,所以render方法中的this指向类的实例。
- 将render返回的虚拟DOM转为真实DOM,随后呈现在页面中。
- 如果在类中定义了方法,这些方法是定义在类的原型上的
3.3 用户定义的组件必须以大写字母开头
以小写字母开头的元素代表一个 HTML 内置组件,比如
或者 会生成相应的字符串 ‘div’ 或者 ‘span’ 传递给 React.createElement(作为参数)。大写字母开头的元素则对应着在 javascript 引入或自定义的组件,如 会编译为 React.createElement(Foo)。
如果你确实需要一个以小写字母开头的组件,则在 JSX 中使用它之前,必须将它赋值给一个大写字母开头的变量。
4. 抽离组件为独立的JS文件
- 创建组件的js文件,如Hello.js
- 在Hello.js文件中导入React
- 创建组件(函数组件或类组件)
- 导出组件
- 在index.js中导入组件
- 渲染组件
创建Hello.js组件文件
import React from 'react';
class Hello extends React.Component
render()
return (
<h1>我是组件的标题</h1>
)
// 导出组件
export default Hello
在index.js文件中导入组件
import React from 'react';
import ReactDOM from 'react-dom';
// 导入组件
import Hello from './components/Hello'
// 渲染
ReactDOM.render(<Hello/>, document.getElementById('root'))
5. react事件处理
5.1 事件绑定
react事件绑定语法与DOM事件语法类似。
(1)通过 onXxx属性指定事件处理函数(注意大小写)。React使用的是自定义(合成)事件。而不是使用的原生DOM事件,目的是为了更好的兼容性
(2)React中的事件是通过事件委托方式处理的(委托给组件最外层的元素),目的是为了高效
(3)通过event.target得到发生事件的dom元素对象
语法:on + 事件名称 = 事件处理程序,如onClick = () => ,react事件采用驼峰命名法,如onMouseEnter
类组件中的事件绑定
class App extends React.Component
handleClick()
console.log('单击事件触发了');
render()
return (
<button onClick=this.handleClick>点击按钮</button>
)
// 渲染
ReactDOM.render(<App/>, document.getElementById('root'))
函数组件中的事件绑定
function App2()
function handleClick()
console.log('单击');
return (
<button onClick=handleClick>按钮</button>
)
// 渲染
ReactDOM.render(<App2 />, document.getElementById('root'))
5.2 事件对象
- 可以通过事件处理程序的参数获取到事件对象
- react中的事件对象叫做:合成事件(对象)
- 合成事件:兼容所有的浏览器,无须担心跨浏览器兼容性问题
class App extends React.Component
handleClick(e)
// 阻止事件的默认行为
e.preventDefault();
console.log('单击事件触发了');
render()
return (
<a href='https://www.baidu.com' onClick=this.handleClick>百度</a>
)
// 渲染
ReactDOM.render(<App />, document.getElementById('root'))
6. 有状态组件和无状态组件
- 函数组件又叫做无状态组件,函数组件没有自己的状态,只负责数据的静态展示
- 类组件又叫做有状态组件,类组件有自己的状态,负责更新UI,让页面动起来
- **状态(state)**就是数据
7. React.PureComponent
React.PureComponent 与 React.Component 很相似。两者的区别在于 React.Component 并未实现 shouldComponentUpdate(),而** React.PureComponent 中以浅层对比 prop 和 state 的方式来实现了该函数。**
如果赋予 React 组件相同的 props 和 state,render() 函数会渲染相同的内容,那么在某些情况下使用 React.PureComponent 可提高性能。
注意
React.PureComponent 中的 shouldComponentUpdate() 仅作对象的浅层比较。如果对象中包含复杂的数据结构,则有可能因为无法检查深层的差别,产生错误的比对结果。仅在你的 props 和 state 较为简单时,才使用 React.PureComponent,或者在深层数据结构发生变化时调用 forceUpdate() 来确保组件被正确地更新。
此外,React.PureComponent 中的 shouldComponentUpdate() 将跳过所有子组件树的 prop 更新。因此,请确保所有子组件也都是“纯”的组件。
8. 组件的state和setState
8.1 state
- 状态state就是数据,是组件内部的私有数据,只能在组件内部使用
- state的值是对象,表示一个组件中可以有多少数据
- 通过this.state来获取状态
class App extends React.Component
constructor()
super();
// 初始化state
this.state =
count: 0
render()
return (
<div>计数器:this.state.count</div>
)
class App2 extends React.Component
// 简化语法初始化state
state =
count: 0
render()
return (
<div>计数器:this.state.count</div>
)
// 渲染
ReactDOM.render(<App2 />, document.getElementById('root'))
8.2 setState()修改状态
- 状态是可变的
- 语法:this.setState(要修改的数据)
- 注意不要直接通过this.state.xxx = yyy修改state中的数据,这是错误的写法
- setState的作用:1. 修改state 2. 更新UI
- 当你调用 setState() 的时候,React 会把你提供的对象合并到当前的 state。这里的合并是浅合并。
class App2 extends React.Component
// 简化语法初始化state
state =
count: 0
add()
console.log(this); // undefined
this.setState(
count: this.state.count + 1
)
render()
return (
<div>
<h1>计数器:this.state.count</h1>
/* 如果直接这样写会报错, add方法内的this指向是undefined*/
/* <button onClick=this.add>+1</button> */
<button onClick=() =>
this.setState(
count: this.state.count + 1
)
>+1</button>
</div>
)
9. 事件绑定时this的指向
类中的方法默认开启了严格模式,this的指向可能为undefined。如果要让this重新指向当前组件实例,可以使用下边三种方法:
9.1 箭头函数
如果类的方式是通过实例调用的,那么方法中的this就指向当前实例。在绑定监听函数时,如果是这样写<button onClick=this.add>+1</button>
,相当于是将add方法作为点击事件的回调函数。当点击时,是直接取出这个函数执行,并不是通过实例调用的,所以此时this指向是undefined。
class App extends React.Component
state =
count: 0
add()
this.setState(
count: this.state.count + 1
)
render()
return (
<div>
<h1>计数器:this.state.count</h1>
<button onClick=() => this.add()></button>
</div>
)
9.2 Function.prototype.bind()
将事件处理程序中的this绑定到当前组件实例。
class App extends React.Component
constructor()
super();
this.state =
count: 0
// 在constructor中给事件处理方法绑定this,constructor中的this指向当前组件实例
// add方法一开始在类的原型上,下边这个操作相当于给组件实例也添加了一个add方法,
// 当调用add方法的时候,组件实例上的add会覆盖掉类原型上的
this.add = this.add.bind(this);
add()
this.setState(
count: this.state.count + 1
)
render()
return (
<div>
<h1>计数器:this.state.count</h1>
/* 这里的onClick就可以正常调用add方法 */
<button onClick=this.add>+1</button>
</div>
)
9.3 箭头函数形式的class实例方法
实例属性除了定义在constructor()方法里面的this上面,也可以定义在类的最顶层。当使用直接给一个变量赋值的形式时,该变量是直接定义在组件实例上的。下方代码中给add赋值了一个箭头函数,则add是直接定义在组件实例上,没有在原型上。
- 利用箭头函数形式的class实例方法
- 注意:该语法是实验性语法,但是由于babel的存在可以直接使用
class App extends React.Component
constructor()
super();
this.state =
count: 0
add = () =>
this.setState(
count: this.state.count + 1
)
render()
return (
<div>
<h1>计数器:this.state.count</h1>
/* 这里的onClick可以正常调用add方法 */
<button onClick=this.add>+1</button>
</div>
)
10. 表单处理
在一个受控组件中,表单数据是由 React 组件来管理的。另一种替代方案是使用非受控组件,这时表单数据将交由 DOM 节点来处理。
10.1 受控组件
- 受控组件:其值受到react控制的表单元素
- HTML中的元素是可输入的,也就是有自己的状态。而react中可变状态通常保存在state中,并且只能通过setState()方法来修改
- react将state与表单元素值value绑定到一起,由state的值来控制表单元素的值
处理步骤:
- 在state中添加一个状态,作为表单元素的value值(控制表单元素值的来源)
- 给表单元素绑定change事件,将表单元素的值设置为state的值(控制表单元素值的变化)
这种操作类似于vue中的双向数据绑定
class App extends React.Component
state =
text: 'hello'
onInputChange = e =>
console.log(e);
this.setState(
text: e.target.value
)
render()
return (
<input type="text" value=this.state.text onChange=this.onInputChange />
)
10.2 常见受控组件
class App extends React.Component
state =
text: 'hello',
content: '富文本',
city: 'bj',
isChecked: false
onInputChange = e =>
console.log(e);
this.setState(
text: e.target.value
)
onTextareaChange = e =>
this.setState(
content: e.target.value
)
onSelectChange = e =>
this.setState(
city: e.target.value
)
onCheckChange = e =>
console.log(e.target.checked);
this.setState(
isChecked: e.target.checked
)
render()
return (
<div>
/* 文本框 */
<input type="text" value=this.state.text onChange=this.onInputChange />
/* 富文本框 */
<textarea value=this.state.content onChange=this.onTextareaChange></textarea>
/* 下拉框 */
<select value=this.state.city onChange=this.onSelectChange>
<option value="sh">上海</option>
<option value="zz">郑州</option>
<option value="bj">北京</option>
</select>
/* 复选框,有defaultChecked属性,它只在初次渲染的时候起作用 */
<input type="checkbox" checked=this.state.isChecked onChange=this.onCheckChange />
</div>
)
上边的代码中对于每一个表单控件都有一个事件处理程序,对上述代码进行优化,让所有的表单控件共用一个事件处理程序。
- 给表单控件添加name属性,名称与state相同
- 根据表单元素类型获取对应的值
- 在change事件处理程序中通过[name]来修改对应的state
10.3 非受控组件
- 借助于ref,使用原生DOM方式来获取表单元素
- ref的作用,获取DOM或组件
使用步骤:
- 调用React.createRef()方法创建一个ref对象
- 将创建好的ref对象添加到文本框中
- 通过ref对象获取到文本框的值
import React from 'react';
import ReactDOM from 'react-dom';
class App extends React.Component
constructor()
super();
this.textRef = React.createRef();
getText = e =>
console.log(this.textRef.current.value);
render()
return (
<div>
<input type="text" ref=this.textRef />
<button onClick=this.getText>获取文本框的值</button>
</div>
)
// 渲染
ReactDOM.render(<App />, document.getElementById('root'))
10.4 小练习
实现发表评论的组件
import React from 'react';
import ReactDOM from 'react-dom';
class App extends React.Component
state 以上是关于React组件基础的主要内容,如果未能解决你的问题,请参考以下文章