React hooks 是React 16.8中的新增功能。它们使您无需编写类即可使用状态和其他React功能
因为函数组件没有生命周期,没有this,没有state
在16.7(beta)当中函数组件一直被当做纯渲染组件使用
在函数组件中,我们没有 this
,所以我们不能分配或读取 this.state
。我们直接在组件中调用 useState
Hook
基本语法:
import {useState} from "react";
let [state,setState] = useState(0);
let [状态的值, 修改状态的方法] = useState(状态的初始值)
在函数组件中,useEffect
Hook 看做 componentDidMount
,componentDidUpdate
和 componentWillUnmount
这三个函数的组合
基本语法:
import {useEffect} from "react";
useEffect(() => {
console.log('更新或者挂载了');
return () => {
console.log('卸载了');
}
}, [监听要更新的数据])
// App根组件
import React, { useState } from "react";
import Child from './Child';
// 函数组件
function App() {
let [data, setName] = useState({
name: '浩克',
age: 28
})
let [show, setShow] = useState(true)
return <div>
{show ? <Child data={data}></Child> : ''}
<button onClick={() => {
setName({ name: '疯狂浩克', age: 10 })
}}>点我</button>
<button onClick={() => {
setShow(false)
}}>卸载</button>
</div>
}
export default App;
// Child子组件
import React, { useState, useEffect } from "react";
function Child(props) {
let { data } = props
let [age, setAge] = useState(8)
useEffect(() => {
console.log('组件更新或者挂载了');
return () => {
console.log('组件卸载了');
}
}, [data.name])
return <div>
<h1>name:{data.name}</h1>
<h1>age:{age}</h1>
<button onClick={() => {
setAge(++age)
}}>+1</button>
</div>
}
export default Child
当应用变得复杂的时候,就需要分块的进行处理和展示,传统模式下,我们是把整个应用分成了多个页面,然后通过 URL 进行连接。但是这种方式也有一些问题,每次切换页面都需要重新发送所有请求和渲染整个页面,不止性能上会有影响,同时也会导致整个 JavaScript 重新执行,丢失状态。
Single Page Application : 单页面应用,整个应用只加载一个页面(入口页面),后续在与用户的交互过程中,通过 DOM 操作在这个单页上动态生成结构和内容
优点:
缺点:
前端路由只是改变了 URL 或 URL 中的某一部分,但一定不会直接发送请求,可以认为仅仅只是改变了浏览器地址栏上的 URL 而已,JavaScript 通过各种手段处理这种 URL 的变化,然后通过 DOM 操作动态的改变当前页面的结构
目前前端路由主要的模式:
React Router 提供了多种不同环境下的路由库
基于 web 的 React Router 为:react-router-dom
npm安装:
npm i -S react-router-dom
基于 HTML5 History API 的路由组件
使用
在index.js
组件当中引入:
import { BrowserRouter } from 'react-router-dom'
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root')
);
基于 URL Hash 的路由组件
使用
在index.js
组件当中引入:
import { HashRouter} from 'react-router-dom'
ReactDOM.render(
<HashRouter>
<App />
</HashRouter>,
document.getElementById('root')
);
#
号的,而BrowerRouter是包含/
号的通过该组件来设置应用单个路由信息,Route 组件所在的区域就是就是当 URL 与当前 Route 设置的 path 属性匹配的时候,后面 component 将要显示的区域
exact 属性表示路由使用 精确匹配模式,非 exact 模式下 ‘/’ 匹配所有以 ‘/’ 开头的路由
import React, { useState } from 'react';
import { Route } from 'react-router-dom';
import IndexPage from './view';
import AboutPage from './view/about';
import HomePage from './view/home';
import Nav from './component/nav';
function App() {
const [state, setstate] = useState('initialState')
return (
<div>
{/* link组件 只改变URL */}
<Nav></Nav>
{/* path属性 匹配URL */}
<Route path="/" exact component={IndexPage} />
<Route path="/about" exact component={AboutPage} />
<Route path="/about/home" exact component={HomePage} />
</div>
);
}
export default App;
Link 组件用来处理 a 链接 类似的功能(它会在页面中生成一个 a 标签),但设置这里需要注意的,react-router-dom 拦截了实际 a 标签的默认动作,然后根据所有使用的路由模式(Hash 或者 HTML5)来进行处理,改变了 URL,但不会发生请求,同时根据 Route 中的设置把对应的组件显示在指定的位置
to 属性类似 a 标签中的 href
NavLink 与 Link 类似,但是它提供了两个特殊属性用来处理页面导航
当当前 URL 与 NavLink 中的 to 匹配的时候,激活 activeStyle 中的样式
与 activeStyle 类似,但是激活的是 className
import React from "react";
import { NavLink } from "react-router-dom";
export default function Nav() {
return (<nav>
<NavLink to="/" exact activeClassName="active" activeStyle={{ color: 'cyan' }}>首页</NavLink>
<span> | </span>
<NavLink to="/about" exact activeClassName="active-about" activeStyle={{ color: 'orange' }}>关于</NavLink>
<span> | </span>
<NavLink to="/about/home" exact activeClassName="active-home" activeStyle={{ color: 'pink' }}>我的</NavLink>
<span> | </span>
</nav>)
}
从上面的图片中可以看到,点击那个,对应的那个路由的样式以及class名会显示出来,剩下的另外两个不显示。
注意:exact
属性必不可少,如果不写这个属性的话,它的所有样式都会出来
<Route exact path='/' component={Home}>
如果 Route 使用的是 component 来指定组件,那么不能使用 props
<Route exact path='/' render={() => <Home items={this.state.items} />} />
通过 render 属性来指定渲染函数,render 属性值是一个函数,当路由匹配的时候指定该函数进行渲染
为了能给处理上面的动态路由地址的访问,我们需要为 Route 组件配置特殊的 path
就是为了给不同的组件传递不同的参数
该组件只会渲染首个被匹配的组件
import React from "react";
import { Link, useParams } from "react-router-dom";
export default function IndexPage(props) {
console.log(useParams());
return (
<div>
<h1>留言列表</h1>
<Link to="/list/1" >1</Link>
<span> | </span>
<Link to="/list/2" >2</Link>
<span> | </span>
<Link to="/list/3" >3</Link>
<span> | </span>
<Link to="/list/4" >4</Link>
<span> | </span>
<Link to="/list/5" >5</Link>
<span> | </span>
<Link to="/list/6" >6</Link>
</div>
)
}
import React from 'react';
import { Route, Switch } from 'react-router-dom';
import IndexPage from './view';
function App() {
return (
<div>
<Switch>
<Route path="/list/:page" exact render={(props) => {
return <IndexPage {...props} />
}}></Route>
</Switch>
</div>
);
}
export default App;
如果一个组件不是路由绑定组件,那么该组件的 props 中是没有路由相关对象的,虽然我们可以通过传参的方式传入,但是如果结构复杂,这样做会特别的繁琐。幸好,我们可以通过 withRouter 方法来注入路由对象
// AboutPage
import React from "react";
import HomePage from "./home";
export default function AboutPage() {
return (
<div>
<h1>关于</h1>
<HomePage use={"爱蜜莉雅"}></HomePage>
</div>
)
}
// HomePage
import React from "react";
import { withRouter } from "react-router-dom";
function HomePage(props) {
console.log(props);
return <h1>我的</h1>
}
export default withRouter(HomePage)
// App
import React from 'react';
import { Route } from 'react-router-dom';
import AboutPage from './view/about';
function App() {
return (
<div>
<Route path="/about" exact component={AboutPage}></Route>
</div>
);
}
export default App;