React的核心概念
React
是Facebook
开发的一个声明式,高效且灵活的用于构建用户界面的Javascript
库。主要有以下特点:
声明式
:以声明式编写 UI组件化
:以组件的方式组织 UI单向数据流
:数据只能从上到下传递。JSX
:JSX
是对Javascript
语法的一种扩展,官方推荐使用此方式编写React
应用,但不是必须的。Virtual DOM
:采用Virtual DOM
来更新真实的DOM
。
Hello World
main.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
App.jsx
(函数)
import React from 'react';
function App() {
return <div className="App">Hello World</div>;
}
export default App;
App.jsx
(类)
import React, { Component } from 'react';
class App extends Component {
render() {
return <div>Hello World</div>;
}
}
export default App;
在React
中,一般推荐使用JSX
来编写代码,其文件后缀为.jsx
或.tsx
,在文件中可以写HTML
元素。在JSX
文件中必须引入React
,即import React from 'react'
。
通过React
编写的组件最终需要通过react-dom
提供的ReactDom.render
来渲染。
JSX
JSX
是Javascript
的语法扩展,可以在Javascript
中写HTML
。在JSX
中通过{}
可以插入Javascript
变量或任何有效的Javascript
表达式,也可以将JSX
赋值给Javascript
变量,还可以在HTML
元素属性使用{}
来设置属性值。
import React, { useState } from 'react';
function App() {
const [count, setCount] = useState(0);
const onClick = () => setCount(count + 1);
return (
<button
style={{
backgroundColor: '#fff',
border: '1px solid #f2f2f2',
outline: 'none'
}}
onClick={onClick}
>
点击{count}次
</button>
);
}
export default App;
注意:在
JSX
中需要元素的属性写成cameCase(小驼峰命名)
,如:元素的class
属性写成className
,tabindex
写成tabIndex
。
事件处理
React
元素的事件处理和DOM
元素相似,但有几点不同:
React
事件的命名采用小驼峰式(camelCase)
,而不是纯小写。- 使用
JSX
语法时需要传入一个函数作为事件处理函数,而不是一个字符串。 - 不能通过返回
false
的阻止默认行为,必须通过e.preventDefault()
来阻止默认行为。 - 当使用
class
方式编写组件时,this
需要手动绑定,否则this
指向会出问题,可通过内联函数,在内联函数中调用,也可以使用箭头函数规避此问题。 - 给事件函数传递参数时,如果需要合成事件
e
,则需要使用内联函数获取,然后再传递给事件函数。
import React, { Component } from 'react';
class App extends Component {
constructor() {
super();
this.onClick1 = this.onClick1.bind(this);
}
onClick1() {
console.log('onClick1:', this);
}
onClick2() {
console.log('onClick2:', this);
}
onClick3 = () => {
console.log('onClick3:', this);
};
onClick4 = (e, test) => {
console.log('onClick4:', e, test);
};
render() {
return (
<div>
<button onClick={this.onClick1}>onClick1</button>
<button onClick={() => this.onClick2()}>onClick2</button>
<button onClick={this.onClick3}>onClick3</button>
<button onClick={this.onClick4}>onClick4</button>
<button onClick={(e) => this.onClick4(e, 111)}>onClick5</button>
</div>
);
}
}
export default App;
条件渲染
React
中条件渲染非常简单,通过Javascript
中的if
或三元运算
来实现,如果不准备渲染任何内容可以设置为null
。
import React, { useState } from 'react';
function App() {
const [visible, setVisible] = useState(true);
const onClick = () => setVisible(!visible);
let content = null;
if (visible) {
content = <div>显示的内容1</div>;
} else {
content = null;
}
return (
<div>
<button onClick={onClick}>改变状态</button>
{content}
{visible ? <div>显示的内容2</div> : null}
</div>
);
}
export default App;
列表渲染
在React
中通过将数据列表转为元素列表来实现列表渲染,一般可以通过Array.map
来实现,在进行列表渲染时需要指定每一项的 key,且 key 应该是唯一的,以便数据发生更改时可以正确的渲染。
import React, { useState } from 'react';
function App() {
const [list, setList] = useState([]);
setTimeout(() => {
setList([1, 2, 3, 4]);
}, 2000);
const elList = list.map((item) => <div key={`${item}_el`}>{item}</div>);
return (
<div>
{list.length === 0 ? 'loading...' : list.map((item) => <div key={item}>{item}</div>)}
{elList}
</div>
);
}
export default App;
受控组件
将React
中的state
作为唯一的数据源,渲染表单的组件控制着用户输入时表单发生的操作,以此种方式控制取值的表单输入元素叫做受控组件。而表单的数据由DOM
节点处理的组件叫做非受控组件。
import React, { ChangeEvent, useState } from 'react';
function App() {
const [value, setValue] = useState('');
const onChange = (e: ChangeEvent<HTMLInputElement>) => {
setValue(e.target.value);
};
return (
<div>
<input type="text" value={value} onChange={onChange} />
<p>输入的值为:{value}</p>
</div>
);
}
export default App;
组件
组件是将 UI 拆分为独立可复用的代码片段。React
组件可以定义为class
和函数
的形式
class
:类需要继承于React.Component
,且必须实现render
函数,在render
函数中返回要渲染的HTML
。函数
:只需函数中返回要渲染的HTML
即可。
State 和 Props
组件和函数类似,接受任意的属性(props)
,然后在组件中就可以使用了,有时组件内需要有自己的数据,称为状态(State)
。在组件中,props
是不可变的,只能通过父组件修改,如果需要在组件内修改prop
则可通过传入一个修改函数,来修改父组件中的state
。state
不能直接修改,直接修改的话视图不会更新渲染,需要通过this.setState
来修改,函数式组件需要通过Hooks
来修改。如果state
需要根据其他的state
来更新的话,可以向setState
传入一个函数,函数的第一个参数是state
,第二个参数是prop
。
User.jsx
import React from 'react';
function User(props) {
const onClick = () => {
props.changeName('codebook');
};
return (
<div onClick={onClick}>
<img src={props.avatar} />
<div>{props.nickname}</div>
</div>
);
}
export default User;
App.jsx
import React, { useState } from 'react';
import User from './components/User';
function App() {
const [name, setName] = useState('React');
return (
<div>
<User
avatar="https://codebook.vercel.app/assets/images/logo.png"
nickname={name}
changeName={setName}
/>
</div>
);
}
export default App;
组件的生命周期
组件从创建到销毁的过程叫做生命周期。
每个组件都有以下生命周期(按照调用顺序排序):
组件创建时:
constructor()
:构造函数,组件挂载之前调用。static getDerivedStateFromProps()
:在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。它应返回一个对象来更新 state,如果返回 null 则不更新任何内容。render()
:渲染函数componentDidMount()
:渲染完成函数。在组件挂载后(插入 DOM 树中)立即调用。
组件更新时:
static getDerivedStateFromProps()
shouldComponentUpdate()
:当props
或state
发生变化时,在渲染执行之前被调用,首次渲染或使用forceUpdate()
时不会调用该方法。默认返回true
,如果返回false
,则不会调用render()
更新视图。render()
getSnapshotBeforeUpdate()
:在最近一次渲染输出(提交到 DOM 节点)之前调用,任何返回值将作为参数传递给componentDidUpdate()
。componentDidUpdate()
:在更新后会被立即调用,首次渲染不会执行此方法。
组件卸载时
componentWillUnmount()
:当组件从 DOM 中移除时会调用此方法。
组件发生错误时
static getDerivedStateFromError()
:在后代组件抛出错误后被调用。 它将抛出的错误作为参数,并返回一个值以更新state
。componentDidCatch()
:生命周期在后代组件抛出错误后(在“提交”阶段被调用,因此允许执行副作用)被调用。 它接收两个参数:error
:抛出的错误。info
:带有componentStack key
的对象
import React, { Component } from 'react';
import User from './components/User';
class App extends Component {
constructor() {
console.log('constructor');
super();
this.state = {
count: 0
};
}
componentDidMount() {
console.log('componentDidMount');
}
shouldComponentUpdate() {
console.log('shouldComponentUpdate');
return true;
}
getSnapshotBeforeUpdate() {
console.log('getSnapshotBeforeUpdate');
return null;
}
componentDidUpdate() {
console.log('componentDidUpdate');
}
componentWillUnmount() {
console.log('componentWillUnmount');
}
onClick = () =>
this.setState({
count: this.state.count + 1
});
render() {
console.log('render');
return <button onClick={this.onClick}>点击了{this.state.count}次</button>;
}
}
export default App;
组件属性默认及验证
如果封装组件时需要指定组件默认,可通过
defaultProps
来指定,需要对组件属性做验证可通过propTypes
并搭配prop-types
库来验证
import React from 'react';
import PropTypes from 'prop-types';
interface ButtonProps {
type: string;
children: any;
}
const Button = (props: ButtonProps) => {
return <button className={`button--${props.type}`}>{props.children}</button>;
};
Button.defaultProps = {
type: 'default'
};
Button.propTypes = {
type: PropTypes.string
};
const App = () => {
return (
<>
<Button>默认按钮</Button>
<Button type="primay">主要按钮</Button>
<Button type="waring">错误按钮</Button>
<Button type={1}>此按钮会在控制台报错,因为type类型不对</Button>
</>
);
};
export default App;
组合组件
React
不建议组件继承,如果需要多个组件结合,可以尝试组合组件,将 UI 拆分成多个组件,通过组合形成不同的 UI。