一、安装
1.使用 create-react-app 快速构建 React 开发环境,并启动
npx create-react-app hello-react
cd hello-react
npm start
2.注意:创建项目反应慢,是因为在寻找相关配置registry时失败了,默认使用了npm安装
解决方法如下:
将npm的register设置为cnpm:
npm config set registry https://registry.npm.taobao.org
执行之后检查是否设置成功
npm config get registry
若返回 https://registry.npm.taobao.org ,则设置成功,再创建react项目。
二、元素渲染(两种方式)
1.ReactDOM.render()函数渲染
const element = <h1>Hello, world!</h1>;
ReactDOM.render(
element,
document.getElementById('example')
);
2.React.Component渲染
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
</div>
);
}
}
三、jsx
特征:有<>就是xml,有{}就是js表达式
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('example')
);
四、组件(两种)
注意:组件只包含一个顶层标签,命名首字母大写
组件分为有状态组件和无状态组件:
1.函数组件(无状态组件):不可以拥有状态,不能拥有生命周期,通过属性实现数据传递(props.X)
//ES5写法
function HelloMessage(props) {
return <h1>Hello World!</h1>;
}
//ES6写法
const HelloMessage = (props) => {
return <h1>Hello World!</h1>;
}
const element = <HelloMessage />;
ReactDOM.render(
element,
document.getElementById('example')
);
2.class组件(有状态组件):可以拥有状态,可以拥有生命周期,通过this接收状态和属性(this.state.X、 this.props.X)
class Welcome extends React.Component {
render() {
return <h1>Hello World!</h1>;
}
}
五、props
props用于传递组件间传递数据
注意:props是不可变的,不能被修改的,只能读取。
1.函数组件中通过props属性传递数据:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
2.class组件中通过this.props传递数据(可用defaultProps设置默认props)
import React, { Component } from 'react'
class DefaultProps extends Component {
render() {
return (
<div>
<p>hello, {this.props.name}</p>
</div>
)
}
}
DefaultProps.defaultProps = {
name: 'dsd'
}
export default DefaultProps
使用propTypes属性对props进行类型检查:
验证器:prop-types 库
详细验证器:https://react.docschina.org/docs/typechecking-with-proptypes.html?
import React, { Component } from 'react'
import PropTypes from 'prop-types'
class DefaultProps extends Component {
render() {
return (
<div>
<p>hello, {this.props.name}</p>
</div>
)
}
}
DefaultProps.defaultProps = {
name: 'dsd'
}
DefaultProps.propTypes = {
name: PropTypes.string
}
export default DefaultProps
六、state
state用于改变组件内部数据。
注意:函数组件是无状态组件,没有state,只有class组件才有state
通过this.setState()修改state的值:
import React, { Component } from 'react'
class StateDemo extends Component {
constructor(props) {
super(props);
this.state = {
name: '小红'
}
}
changState = () => {
this.setState({
name: '小明'
})
}
render() {
return (
<div>
<p>{this.state.name}</p>
<button onClick={this.changState}>修改state</button>
</div>
)
}
}
export default StateDemo
七、生命周期
注意:只有class组件才有生命周期,函数组件没有。
-
componentWillMount 在渲染前调用,在客户端也在服务端。
-
componentDidMount : 在第一次渲染后调用,只在客户端。之后组件已经生成了对应的DOM结构,可以通过this.getDOMNode()来进行访问。 如果你想和其他JavaScript框架一起使用,可以在这个方法中调用setTimeout, setInterval或者发送AJAX请求等操作(防止异步操作阻塞UI)。
-
componentWillReceiveProps 在组件接收到一个新的 prop (更新后)时被调用。这个方法在初始化render时不会被调用。
-
shouldComponentUpdate 返回一个布尔值。在组件接收到新的props或者state时被调用。在初始化时或者使用forceUpdate时不被调用。
可以在你确认不需要更新组件时使用。 -
componentWillUpdate在组件接收到新的props或者state但还没有render时被调用。在初始化时不会被调用。
-
componentDidUpdate 在组件完成更新后立即调用。在初始化时不会被调用。
-
componentWillUnmount在组件从 DOM 中移除之前立刻被调用。
八、事件处理
1.命名:采用小驼峰命名法onClick、onChange
2.调用时注意this指向问题,事件调用方法,方法中的this指向调用事件的对象(2种写法)
(1)用bind()绑定this:
changState是ClickDemo类的方法,按钮点击事件调用changState,changState中的this默认指向button,要绑定this指向ClickDemo类才能正确执行代码中的this.setState
import React, { Component } from 'react'
class ClickDemo extends Component {
constructor(props) {
super(props);
this.state = {
name: '小红'
}
// 绑定this指向ClickDemo
this.changState = this.changState.bind(this);
}
changState(){
this.setState({
name: '小明'
})
}
render() {
return (
<div>
<p>{this.state.name}</p>
<button onClick={this.changState}>修改state</button>
</div>
)
}
}
export default ClickDemo
(2)class fields 方法:箭头函数中的this在定义时就确定为ClickDemo
import React, { Component } from 'react'
class ClickDemo extends Component {
constructor(props) {
super(props);
this.state = {
name: '小红'
}
}
// 箭头函数中的this在定义时就确定为ClickDemo
changState = () => {
this.setState({
name: '小明'
})
}
render() {
return (
<div>
<p>{this.state.name}</p>
<button onClick={this.changState}>修改state</button>
</div>
)
}
}
export default ClickDemo
九、条件渲染
1.if语句
import React, { Component } from 'react'
class IfDemo extends Component {
constructor(props) {
super(props);
this.state = {
isLoggin: false
}
}
render() {
let Tip;
if(this.state.isLoggin) {
Tip = <p>您已经登陆</p>
} else {
Tip = <p>您还未登陆</p>
}
return (
<div>
{Tip}
</div>
)
}
}
export default IfDemo
2.&&运算符
import React, { Component } from 'react'
class AndDemo extends Component {
constructor(props) {
super(props);
this.state = {
list: [1,2,3]
}
}
render() {
let Tip;
return (
<div>
{this.state.list.length > 0 &&
<h2>你有{this.state.list.length}个元素</h2>
}
</div>
)
}
}
export default AndDemo
3.三目运算符
import React, { Component } from 'react'
class AndDemo extends Component {
constructor(props) {
super(props);
this.state = {
list: []
}
}
render() {
let Tip;
return (
<div>
{this.state.list.length > 0 ?
<h2>你有{this.state.list.length}个元素</h2> :
<h2>你没有元素</h2>
}
</div>
)
}
}
export default AndDemo
十、列表渲染
通常使用map()遍历数组,每项设置key来区分
import React, { Component } from 'react'
class List extends Component {
constructor(props) {
super(props);
this.state = {
list: [
{name: '小红',age: 11},
{name: '小明',age: 51},
{name: '小黑',age: 71}
]
}
}
render() {
return (
<div>
{this.state.list.map((item, index) => {
return <p key={index}>{item.name}今年{item.age}岁</p>
})}
</div>
)
}
}
export default List
十一、受控组件
1.input(textarea写法相同)
import React, { Component } from 'react'
class List extends Component {
constructor(props) {
super(props);
this.state = {
name: ''
}
}
changeName = (e) => {
console.log(e.target.value)
this.setState({
name: e.target.value
})
}
render() {
return (
<form>
<div>
<label>name:</label>
<input type="text" value={this.state.name} onChange={this.changeName} />
</div>
</form>
)
}
}
export default List
2.多个input
import React, { Component } from 'react'
class List extends Component {
constructor(props) {
super(props);
this.state = {
name: '',
age: ''
}
}
changeInput = (e) => {
console.log(e.target.value)
this.setState({
[e.target.name]: e.target.value
})
}
render() {
return (
<form>
<div>
<label>name:</label>
<input name="name" type="text" value={this.state.name} onChange={this.changeInput} />
</div>
<div>
<label>age:</label>
<input name="age" type="text" value={this.state.age} onChange={this.changeInput} />
</div>
</form>
)
}
}
export default List
3.select
import React, { Component } from 'react'
class SelectDemo extends Component {
constructor(props) {
super(props);
this.state = {
age: '12'
}
}
changeSelected = (e) => {
console.log(e.target.value)
this.setState({
age: e.target.value
})
}
render() {
return (
<form>
<label>年龄</label>
<select value={this.state.age} onChange={this.changeSelected}>
<option value="11">11</option>
<option value="12">12</option>
<option value="13">13</option>
</select>
</form>
)
}
}
export default SelectDemo
十二、ref
注意:
- 当
ref
属性用于 HTML 元素时,ref
接收底层 DOM 元素作为其current
属性。 - 当
ref
属性用于自定义 class 组件时,ref
对象接收组件的挂载实例作为其current
属性。 - 你不能在函数组件上使用
ref
属性,因为他们没有实例。
使用:Refs 是使用 React.createRef()
创建的,并通过 ref
属性附加到 React 元素。在构造组件时,通常将 Refs 分配给实例属性,以便可以在整个组件中引用它们。当 ref 被传递给 render
中的元素时,对该节点的引用可以在 ref 的 current
属性中被访问。
import React, { Component } from 'react'
class RefDemo extends Component {
constructor(props) {
super(props);
this.myref = React.createRef()
}
getRef = () => {
console.log(this.myref.current)
}
render() {
return (
<div>
<p ref={this.myref}>小红</p>
<button onClick={this.getRef}>获取ref</button>
</div>
)
}
}
export default RefDemo
十三、路由(react-router-dom)
1.安装
cnpm i --save react-router-dom
2.基本实现
import React from 'react';
import SelectDemo from './components/SelectDemo'
import { Switch, BrowserRouter as Router, Route, NavLink, Link } from 'react-router-dom'
function App() {
return (
<div className="App">
{/* 将路由的组件用<Router>包裹起来 */}
<Router>
<nav>
<ul>
<li>
{/* <Link to="">链接的使用 */}
<Link to="/select">select</Link>
</li>
<li>
<Link to="/refdemo">refdemo</Link>
</li>
<li>
<NavLink exact to="/demo">demo</NavLink>
</li>
</ul>
</nav>
{/* <Switch>是指页面只出现下面路由中的一个路由 */}
<Switch>
{/* <Route path="">链接的设置(载入页面有3种写法) */}
<Route path="/select" component={SelectDemo}></Route>
<Route path="/refdemo"><RefDemo /></Route>
<Route path="/demo" render={()=><p>demo</p>}></Route>
</Switch>
</Router>
</div>
);
}
export default App;
3.导航链接 高亮
导航链接是使用链接时将<Link>改为<NavLink>,选中时会添加默认类active,自行在App.css中加入.active的样式即可
import React from 'react';
import SelectDemo from './components/SelectDemo'
import { Switch, BrowserRouter as Router, Route, NavLink, Link } from 'react-router-dom'
import './App.css'
function App() {
return (
<div className="App">
{/* 将路由的组件用<Router>包裹起来 */}
<Router>
<nav>
<ul>
<li>
{/* <NavLink to="">链接的使用 */}
<NavLink to="/select">select</NavLink>
</li>
<li>
<NavLink to="/refdemo">refdemo</NavLink>
</li>
</ul>
</nav>
{/* <Switch>是指页面只出现下面路由中的一个路由 */}
<Switch>
{/* <Route path="">链接的设置(载入组件有两种写法) */}
<Route path="/select" component={SelectDemo}></Route>
<Route path="/refdemo"><RefDemo /></Route>
</Switch>
</Router>
</div>
);
}
export default App;
4、exact属性:匹配规则,当有链接是/,而其他链接是/select之类时,为了不匹配到/,则在链接设置和使用时都加入exact
import React from 'react';
import './App.css';
import List from './components/List'
import SelectDemo from './components/SelectDemo'
import RefDemo from './components/RefDemo'
import { Switch, BrowserRouter as Router, Route, NavLink, Link } from 'react-router-dom'
function App() {
return (
<div className="App">
<Router>
<nav>
<ul>
<li>
{/* 为了在选中/select时不匹配到/链接,使用exact属性 */}
<NavLink exact to="/select">select</NavLink>
</li>
<li>
<NavLink exact to="/refdemo">refdemo</NavLink>
</li>
<li>
<NavLink exact to="/">List</NavLink>
</li>
</ul>
</nav>
<Switch>
{/* 使用exact属性 */}
<Route exact path="/select" component={SelectDemo}></Route>
<Route exact path="/refdemo"><RefDemo /></Route>
<Route exact path="/" component={List}></Route>
</Switch>
</Router>
</div>
);
}
export default App;
5、strict属性:精准匹配,当链接完全匹配时才能跳转
6、404页面设置:在所有设置链接后添加一个没有path属性的<Route>,表示匹配所有路由,当链接没有被设置时就自动匹配这个路由
import React from 'react';
import './App.css';
import List from './components/List'
import SelectDemo from './components/SelectDemo'
import RefDemo from './components/RefDemo'
import { Switch, BrowserRouter as Router, Route, NavLink, Link } from 'react-router-dom'
import NotFound from './components/NotFound';
function App() {
return (
<div className="App">
<Router>
<nav>
<ul>
<li>
<NavLink exact to="/select">select</NavLink>
</li>
<li>
<NavLink exact to="/refdemo">refdemo</NavLink>
</li>
<li>
<NavLink exact to="/">List</NavLink>
</li>
</ul>
</nav>
<Switch>
<Route exact path="/select" component={SelectDemo}></Route>
<Route exact path="/refdemo"><RefDemo /></Route>
<Route exact path="/" component={List}></Route>
{/* 在设置路由最后加入没有path的路由,匹配前方没有设置的路由(404) */}
<Route component={NotFound}></Route>
</Switch>
</Router>
</div>
);
}
export default App;
7.路由模式:history模式和hash模式
切换路由的模式只需引入BrowserRouter或HashRouter
import { BrowserRouter as Router } from 'react-router-dom'
或者
import { HashRouter as Router } from 'react-router-dom'
8、获取路由参数
(1)params参数
App.js:设置和使用路由
import React from 'react';
import './App.css';
import Param from './components/Param'
import { Switch, BrowserRouter as Router, Route, NavLink, Link } from 'react-router-dom'
import NotFound from './components/NotFound';
function App() {
return (
<div className="App">
<Router>
<nav>
<ul>
<li>
{/* 使用路由 */}
<NavLink exact to="/param/user/1">param</NavLink>
</li>
</ul>
</nav>
<Switch>
{/* 设置路由 */}
<Route path="/param/user/:id" component={Param}></Route>
<Route component={NotFound}></Route>
</Switch>
</Router>
</div>
);
}
export default App;
Param.js:通过props.match获取路由参数:
import React, { Component } from 'react'
class Param extends Component {
componentDidMount() {
console.log(this.props.match)
}
render() {
return (
<div> param </div>
)
}
}
export default Param
(2)query参数(两种方法)
App.js:
import React from 'react';
import './App.css';
import Query from './components/Query'
import { Switch, BrowserRouter as Router, Route, NavLink, Link } from 'react-router-dom'
import NotFound from './components/NotFound';
function App() {
return (
<div className="App">
<Router>
<nav>
<ul>
<li>
{/* 使用路由 */}
<NavLink exact to="/query/user?id=1">param</NavLink>
</li>
</ul>
</nav>
<Switch>
{/* 设置路由 */}
<Route path="/query/user" component={Query}></Route>
<Route component={NotFound}></Route>
</Switch>
</Router>
</div>
);
}
export default App;
方法一、使用URLSearchParams()
import React, { Component } from 'react'
class Query extends Component {
render() {
const query = new URLSearchParams(this.props.location.search)
console.log(query.get("id")); // 1
return (
<div> query </div>
)
}
}
export default Query
方法二、使用Node中的querystring.parse()解析路由
注意:获取到的第一个数据有带着?
import React, { Component } from 'react'
import querystring from 'querystring'
class Query extends Component {
render() {
const query = querystring.parse(this.props.location.search)
console.log(query); // {?id:1}
return (
<div> query </div>
)
}
}
export default Query
9.重定向
1.使用<Redirect from="" to="">进行重定向
import React from 'react';
import './App.css';
import List from './components/List'
import { Switch, BrowserRouter as Router, Route, NavLink, Link, Redirect } from 'react-router-dom'
import NotFound from './components/NotFound';
function App() {
return (
<div className="App">
<Router>
<nav>
<ul>
<li>
{/* 使用路由 */}
<NavLink exact to="/mine">重定向</NavLink>
</li>
</ul>
</nav>
<Switch>
<Redirect from="/mine" to="/list" />
<Route path="/list" component={List}></Route>
<Route component={NotFound}></Route>
</Switch>
</Router>
</div>
);
}
export default App;
2.使用props.history.push()进行重定向
App.js:
import React from 'react';
import './App.css';
import { Switch, BrowserRouter as Router, Route, NavLink, Link, Redirect } from 'react-router-dom'
import NotFound from './components/NotFound';
import Push from './components/Push';
import List from './components/List'
function App() {
return (
<div className="App">
<Router>
<Switch>
{/* 重定向的两个路由需定义才有路由对象(即props.history) */}
<Route path="/list" component={List}></Route>
<Route path="/push" component={Push}></Route>
<Route component={NotFound}></Route>
</Switch>
</Router>
</div>
);
}
export default App;
Push.js:
import React, {Component} from 'react'
const PushDemo = (props) => {
const toOther = () => {
console.log(props)
props.history.push('/list')
}
return (
<div>
<button onClick={toOther}>重定向</button>
</div>
)
}
export default PushDemo
3.使用props.history.replace()进行重定向(写法与props.history.push()一样)
10.withRouter:为未设为路由的组件添加路由对象
App.js:
import React from 'react';
import './App.css';
import { Switch, BrowserRouter as Router, Route, NavLink, Link, Redirect } from 'react-router-dom'
import Push from './components/Push';
function App() {
return (
<div className="App">
{/* 被添加路由对象的组件需放在<Router>中 */}
<Router>
<Push />
</Router>
</div>
);
}
export default App;
push.js:
import React, {Component} from 'react'
import {withRouter} from 'react-router-dom'
const PushDemo = (props) => {
const toOther = () => {
console.log(props)
props.history.push('/list')
}
return (
<div>
<button onClick={toOther}>重定向</button>
</div>
)
}
// withRouter()为组件添加路由对象
export default withRouter(PushDemo)
11.Prompt:离开页面之前提示用户
App.js:
import React from 'react';
import './App.css';
import { Switch, BrowserRouter as Router, Route, NavLink, Link, Redirect } from 'react-router-dom'
import List from './components/List';
import Prompt from './components/Prompt';
function App() {
return (
<div className="App">
{/* 拥有的组件需放在<Router>中 */}
<Router>
<nav>
<NavLink to="/list">list</NavLink>
</nav>
<Switch>
<Route path="/list"><List></List></Route>
</Switch>
<Prompt / >
</Router>
</div>
);
}
export default App;
Prompt.js:
import React, { Component } from 'react'
import {Prompt} from 'react-router-dom'
class PromptDemo extends Component {
constructor(props) {
super(props);
this.state = {
name: ''
}
}
changeName = (e) => {
this.setState({
name: e.target.value
})
}
render() {
return (
<div>
<input value={this.state.name} type="text" onChange={this.changeName} />
{/* 当name不为空时,离开页面会有prompt弹窗提示 */}
<Prompt when={!!this.state.name} message={'确定要离开当前页面吗?'}></Prompt>
</div>
)
}
}
export default PromptDemo
12.嵌套路由
App.js
import React from 'react';
import './App.css';
import { Switch, BrowserRouter as Router, Route, NavLink, Link, Redirect } from 'react-router-dom'
import Book from './components/Book/Book';
import Java from './components/Book/JavaBook';
import Web from './components/Book/WebBook';
function App() {
return (
<div className="App">
{/* 拥有的组件需放在<Router>中 */}
<Router>
<nav>
<NavLink to="/book/java">java</NavLink>
<NavLink to="/book/web">web</NavLink>
</nav>
<Switch>
<Book>
<Route path="/book/java" component={Java}></Route>
<Route path="/book/web" component={Web}></Route>
</Book>
</Switch>
</Router>
</div>
);
}
export default App;
Book.js:
import React, { Component } from 'react'
class Book extends Component {
render() {
return (
<div>
<h2>book</h2>
{/* 使用props.children放置子路由的内容 */}
{this.props.children}
</div>
)
}
}
export default Book
JavaBook.js:
import React from 'react'
const Java = () => {
return (
<div>Java</div>
)
}
export default Java
WebBook.js
import React from 'react'
const Web = () => {
return (
<div>Web</div>
)
}
export default Web
十四、错误处理
错误处理:组件报错时,关掉页面错误后显示组件发生错误字样
1.创建处理错误的组件components/ErrorCatch.js: 使用componentDidCatch()捕获错误
import React from 'react'
class ErrorCatch extends React.Component {
constructor(props) {
super(props)
this.state = {
hasError: false,
error: null,
errorInfo: null
}
}
componentDidCatch(error, errorInfo) {
console.log(error);
this.setState({
hasError: true,
error: error,
errorInfo: errorInfo
})
}
render() {
if(this.state.hasError) {
return <div> { this.props.render(this.state.error, this.state.errorInfo) } </div>
}
return this.props.children;
}
}
export default ErrorCatch
2.创建发生错误的组件components/Error.js:
import React from 'react'
const Error = () => {
return (
<div>
{null.map((item,index) => {
console.log(item)
})}
</div>
)
}
export default Error
3.App.js中引入:
import React from 'react';
import logo from './logo.svg';
import './App.css';
import Error from './components/Error'
import ErrorCatch from './components/ErrorCatch'
class App extends React.Component {
render() {
return (
<div>
<ErrorCatch render={(error, errorInfo) => <p>{'组件发生错误'}</p>}>
<Error />
</ErrorCatch>
</div>
)
}
}
export default App;
十五、shouldComponentUpdate()优化组件
对于不必一直进行更新的组件,在shouldComponentUpdate(nextProps,nextState)判断其props或state是否更新,若更新,则进行组件更新,若无更新,则阻止组件更新。
Interval.js:
import React from 'react'
import Child from './Child'
class Interval extends React.Component {
timer = null
constructor(props) {
super(props);
this.state = {
count: 0
}
}
componentDidMount() {
this.timer = setInterval(()=> {
this.setState({
count: this.state.count + 1
})
}, 1000)
}
// 要在离开组件之前关闭定时器,避免组件卸载后继续更新state而报错
componentWillUnmount(){
console.log(123)
clearInterval(this.timer)
}
render() {
console.log('interval')
return (
<div>
{this.state.count}
<Child num="2"></Child>
</div>
)
}
}
export default Interval
Child.js:
import React, { Component } from 'react'
class Child extends Component {
// 当传入Child组件的值没有改变时,Child组件不重新渲染
shouldComponentUpdate(nextProps,nextState) {
if(this.props.num == nextProps.num) {
return false;
}
return true;
}
render() {
console.log('child')
return (
<div>Child</div>
)
}
}
export default Child
十六、PureComponent
PureComponent对数据进行浅比较,自动安排是否需要重新渲染
注意:用在class组件,如果对象中包含复杂的数据结构,则有可能因为无法检查深层的差别,产生错误的比对结果。
Demo:将上个例子中的Child.js进行改造,获得同样效果,即child组件的num没有变化时,child组件不重新渲染
import React, { PureComponent } from 'react'
class Child extends PureComponent {
render() {
console.log('child')
return (
<div>Child</div>
)
}
}
export default Child
十七、React.memo()
注意:与React.PureComponent非常相似,但它适用于函数组件,但不适用于 class 组件。
import React from 'react'
const Memo = (props) => {
console.log(props.num);
return (
<div>
{props.num}
</div>
)
}
export default React.memo(Memo)
十八、Fragment:虚标签
<Fragment>编译后无任何显示:
import React, { Component, PureComponent, Fragment } from 'react'
class List extends PureComponent {
render() {
console.log('child')
return (
<ul>
<Fragment>
<li>列表1</li>
</Fragment>
<Fragment>
<li>列表2</li>
</Fragment>
</ul>
)
}
}
export default List
编译后的列表为
<ul>
<li>列表1</li>
<li>列表2</li>
</ul>
十九、Context
Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props。
import React, { createContext, Component } from 'react'
// Top组件经过Middle组件传值给Bottom组件,在Middle组件中不需要用到该值,可使用createContext(),在Bottom组件中接收
const MyContext = createContext();
// Top组件中设置MyContext的值
class Top extends Component {
render() {
return (
<MyContext.Provider value="12222">
<Middle />
</MyContext.Provider>
)
}
}
class Middle extends Component {
render() {
return (
<Bottom />
)
}
}
// Bottom组件中将MyContext赋值给contextType,组件中可通过this.context获取MyContext
class Bottom extends Component {
static contextType = MyContext;
render() {
return (
<p>{this.context}</p>
)
}
}
export default Top
二十、HOOK
1.HOOK作用:在不编写 class 的情况下使用 state 以及其他的 React 特性。
2.基础Demo:
import React,{useState,useCallback} from 'react'
export default () => {
// useState(0):设置count的默认值为0
// setCount: 改变count的值
const [count, setCount] = useState(0);
return (
<div>
<p>count:{count}</p>
// 使用setCount()改变count的值
<button onClick={() => {setCount(count+ 1)}}>Add</button>
</div>
)
}
3.useCallback():进行优化,若第二参数中的值没有改变,则不会执行第一个参数中的方法
import React,{useState,useCallback} from 'react'
export default () => {
// useState(0):设置count的默认值为0
// setCount: 改变count的值
const [count, setCount] = useState(0);
const [afterCount, setAfterCount] = useState(0);
return (
<div>
<p>count:{count}</p>
<p>afterCount:{afterCount}</p>
<button onClick={() => {setCount(count+ 1)}}>Add</button>
{/* useCallback(fun,[]):第二个参数值如果改变了,则执行第一个参数中的方法,若没有改变,则不执行 */}
<button onClick={useCallback(() => {setAfterCount(afterCount+ 1)}, [count])}>Add</button>
</div>
)
}
4、userReducer(reducer,initialState):当state 逻辑较复杂且包含多个子值时,可使用userReducer(reducer,initialState),类似于redux
import React, { useReducer } from 'react'
const initialState = {
count: 0
}
const reducer = (state, action) => {
switch(action.type) {
case "increment":
return {count: state.count + 1};
case "decrement":
return {count: state.count - 1};
default:
throw new Error();
}
}
function UseReducerDemo () {
const [state, disptch] = useReducer(reducer, initialState)
return (
<div>
<p>{state.count}</p>
<button onClick={() => disptch({type: 'increment'})}> + </button>
<button onClick={() => disptch({type: 'decrement'})}> - </button>
</div>
)
}
export default UseReducerDemo
5.useContext(context):获取context
Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props。
UseContextParentDemo.js:
import React from 'react'
import UseContextChildDemo from './UseContextChildDemo'
// React.createContext(默认值):创建context
export const MyContext = React.createContext('hello')
// 修改context的值
const UseContextParentDemo = () => {
return(
<MyContext.Provider value="hi">
<UseContextChildDemo />
</MyContext.Provider>
)
}
export default UseContextParentDemo
UseContextChildDemo.js:
import React, { useContext } from 'react'
import {MyContext} from './UseContextParentDemo'
// 使用useContext(context)获取context
const UseContextChildDemo = () => {
return(
<div>
<p>{useContext(MyContext)}</p>
</div>
)
}
export default UseContextChildDemo;
6.useEffect():
可以让你在函数组件中执行副作用操作。
副作用:数据获取,设置订阅以及手动更改react组件中的DOM等。
useEffect(): 代替三个生命周期:componentDidMount、componentDidUpdate、componentWillUnmount
第二个参数决定其代表哪个生命周期:
* 当没有第二个参数时, useEffect() 相当于componentDidMount、 componentDidUpdate
* 当第二个参数为[] 时, 相当于componentDidMount
*[count]: 只有count发生改变时, 才会触发componentDidUpdate
* return:相当于componentWillUnmount
import React, { useState, useEffect } from 'react'
export default () => {
const [count, setCount] = useState(0);
// useEffect(): 代替三个生命周期:componentDidMount、componentDidUpdate、componentWillUnmount
useEffect(() => {
document.title = `你点击了${count}下`;
})
return (
<div>
<p>你点击了{count}下</p>
<button onClick={()=>{setCount(count + 1)}}>+1</button>
</div>
)
}
二十一、State
1.this.setState({})在生命周期中异步执行:
import React, { Component } from 'react'
class StateDemo extends Component {
constructor(props) {
super(props);
this.state = {
count: 0
}
}
// this.setState()在生命周期中异步操作,当执行第一个时,已经往下执行。来不及执行第二第三个
componentDidMount() {
this.setState({
count: this.state.count + 1
})
this.setState({
count: this.state.count + 1
})
this.setState({
count: this.state.count + 1
})
}
// 这里打印出的this.state.count值为1
render() {
return (
<div>{this.state.count}</div>
)
}
}
export default StateDemo
2. 在生命周期中使用this.setState((prevState,props) =>{}),可在生命周期结束前完成执行
import React, { Component } from 'react'
class StateDemo extends Component {
constructor(props) {
super(props);
this.state = {
count: 0
}
}
componentDidMount() {
this.setState((prevState,props) => ({
count: prevState.count + 1
}))
this.setState((prevState, props) => ({
count: prevState.count + 1
}))
this.setState((prevState, props) => ({
count: prevState.count + 1
}))
}
// 这里打印出的this.state.count值为3
render() {
return (
<div>{this.state.count}</div>
)
}
}
export default StateDemo
3.使用this.setState({},()=>{})
import React, { Component } from 'react'
class StateDemo extends Component {
constructor(props) {
super(props);
this.state = {
count: 0
}
}
componentDidMount() {
// 合并所有的异步执行,异步执行完毕后才会执行回调
this.setState({
count: this.state.count
}, () => {
console.log(this.state.count) // 返回4
})
this.setState((prevState,props) => ({
count: prevState.count + 1
}))
this.setState((prevState, props) => ({
count: prevState.count + 1
}))
this.setState((prevState, props) => ({
count: prevState.count + 1
}))
}
// 这里打印出的this.state.count值为4
render() {
return (
<div>{this.state.count}</div>
)
}
}
export default StateDemo
二十二、PubSubJS:消息订阅(subscribe)、发布(publish)机制
工具库:pubsub-js
无论父子组件、兄弟组件,都可以订阅、发布数据
例:子组件要修改父组件数据
子组件:发布消息
handlerDelete = () => {
const { comment, index } = this.props;
if (window.confirm(`确定删除${comment.username}的评论吗?`)) {
PubSub.publish("deleteComment", index);
}
};
父组件:订阅消息(componentDidMount)
componentDidMount() {
PubSub.subscribe("deleteComment", (msg, data) => {
this.deleteComment(data);
});
}
二十三、生命周期
React V16.3生命周期:
React v16.4之后的生命周期:
二十四、createPortal:将组件中定义 的内容插入其他位置
语法: createPortal(child, content)
import React, { Component } from "react";
import { createPortal } from "react-dom";
class DialogPage extends Component {
constructor(props) {
super(props);
this.state = {
showDialog: false
};
}
render() {
const { showDialog } = this.state;
return (
<div>
<button onClick={() => this.setState({ showDialog: !showDialog })}>
toggle dialog
</button>
{showDialog && <Dialog />}
</div>
);
}
}
{
/* 此时Dialog组件不是添加在DialogPage组件中,而是在document.body中 */
}
class Dialog extends Component {
render() {
return createPortal(
<div style={{ border: "1px solid #aaa" }}>dialog</div>,
window.document.body
);
}
}
export default DialogPage;
二十五、高阶组件
高阶组件:参数为组件,返回值为组件。
import React, { Component } from "react";
// 高阶组件:参数为组件,返回值为组件
const foo = Cmp => props => {
return (
<div style={{ border: "1px solid red", padding: "10px" }}>
<Cmp {...props} />
</div>
);
};
function Child(props) {
return <div>Child</div>;
}
const Foo = foo(foo(Child));
class HocPage extends Component {
render() {
return <Foo />;
}
}
export default HocPage;
多层链式嵌套,使用装饰器:(不能使用装饰器时进行配置 yarn add @babel/plugin-proposal-decorators)
import React, { Component } from "react";
// 高阶组件:参数为组件,返回值为组件
const foo = Cmp => props => {
return (
<div style={{ border: "1px solid red", padding: "10px" }}>
<Cmp {...props} />
</div>
);
};
// 装饰器只能用在类组件上
@foo
@foo
class ClassChild extends Component {
render() {
return <div>ClassChild</div>;
}
}
class HocPage extends Component {
render() {
return <ClassChild />;
}
}
export default HocPage;