react3.0
全局导入的变量名可以自定义,按需导入则是要用规定的变量名
复习组件传值:
父传子,子传父:
review.js
import React from "react"
// 传值要注意this指向问题,解决办法有三个:1.在构造函数里面用bind,2.初始化时使用箭头函数,3.在该函数外面套一层箭头函数
// 父组件
class Person extends React.Component {
state = {
msg:'我是父组件的数据'
}
render() {
return (
<div>hello,早上好呀
{ this.state.msg}
<Son msg={this.state.msg} />
{ /*info是Child子组件绑定的属性,属性值是父组件的回调函数,用来接收数据*/}
<Child info={ this.getInfo}/>
</div>
)
}
// 给父组件添加事件,是一个回调函数
// 接收Child传递过来的数据,并重新修改state中msg的值
// 参数msg就是子组件传递过来的数据
getInfo = (msg) => {
this.setState({
msg:msg
})
}
}
// 父传子
// 子组件
function Son(props) {
return (
<div>我是Son子组件,父组件传递过来的数据:{ props.msg}</div>
)
}
class Child extends React.Component {
// 给子组件添加状态
state = {
msg:'我是子组件Child的数据'
}
render() {
return (
<div>
我是Child子组件
{ /*通过触发handle事件,将state中的msg数据传递给父组件*/}
<button onClick={this.handle}>传值,改变父组件的msg</button>
</div>
)
}
handle = () => {
// 通过this.props调用父组件的回调函数
// info是标签的自定义属性,是一个回调函数,参数是要传递的数据
this.props.info(this.state.msg)
}
}
export default Person
index.js
import React from 'react';
import ReactDOM from 'react-dom';
// 子传父,父传子
import Person from "./components/review"
ReactDOM.render(
<div><Person/></div>,
document.getElementById('root')
);
兄弟组件之间传值:
import React from 'react';
import ReactDOM from 'react-dom';
// 兄弟组件之间的传值:子传父,父传子相结合,通过自定义属性传值
// 公共组件Hello
class Hello extends React.Component {
// 公用状态count
state = {
count: 0
}
render() {
return (
<div>
<Son1 info={ this.state.count}/>
<Son2 info={ this.getInfo}/>
</div>
)
}
// 处理count
getInfo = () => {
this.setState({
count:this.state.count+1
})
}
}
// Son1组件:显示count
class Son1 extends React.Component {
render() {
return (
<div>
{ this.props.info}
</div>
)
}
}
// Son2组件:操作count
class Son2 extends React.Component {
render() {
return (
<div>
<button onClick={ this.handle}>+1</button>
</div>
)
}
handle = () => {
this.props.info()
}
}
ReactDOM.render(
<div><Hello /></div>,
document.getElementById('root')
);
Context
import React from 'react';
import ReactDOM from 'react-dom';
// Provider:提供数据,Consumer:使用数据
const {Provider,Consumer } =React.createContext()
// 想要给哪个组件传值,就在该组件标签外面加一个Provider标签,该标签的value属性值就是要传递的数据
class Hello extends React.Component {
state = {
msg:'我是Provider提供的数据'
}
render() {
return (
<div>
<Provider value={this.state.msg}>
我是Provider标签
<Son />
</Provider>
<Provider value="我是一个干饭人,加油,努力干饭">
<Child/>
</Provider>
</div>
)
}
}
// 向使用Provider提供的数据数据
class Son extends React.Component {
render() {
return (
<div>
<Child />
<Consumer>
{data => { return data}}
</Consumer>
</div>
)
}
}
class Child extends React.Component {
render() {
return (
<div>
我是Son组件
<Consumer>{data => { return data}}</Consumer>
</div>
)
}
}
ReactDOM.render(
<div><Hello /></div>,
document.getElementById('root')
);
组件生命周期
组件生命周期:组件从被创建到挂载到页面中运行,再到组件不用时卸载的过程;生命周期的每个阶段总是伴随着一些方法调用,这些方法就是生命周期的钩子函数
只有类组件才有生命周期,函数组件没有生命周期
钩子函数的作用:为开发人员在不同阶段操作组件提供了时机
生命周期的三个阶段
1.创建时(挂载阶段)
执行时机:组件创建时(页面加载时)
钩子函数的执行顺序:constructor(){}——>render(){}——>componentDidMount(){}
钩子函数 | 触发时机 | 作用 |
---|---|---|
constructor | 创建组件时,最先执行 | 1.初始化state;2.为事件处理程序绑定this |
render | 每次组件渲染都会触发 | 渲染UI(注意:不能调用setState()) |
componentDidMount | 组件挂载(完成DOM渲染)后 | 1.发送网络请求;2.DOM操作 |
2.更新时(更新阶段)
执行时机:1.setState();2.forceUpdate();3.组件接收到新的props
说明:以上三者任意一种变化,组件就会重新渲染
执行顺序:render()——>componentDidUpdate()
钩子函数 | 触发时机 | 作用 |
---|---|---|
render | 每次组件渲染都会触发 | 渲染UI(与挂载阶段是同一render) |
componentDidUpdate | 组件更新(完成DOM渲染后) | 1.发送网络请求;2.DOM操作 注意:如果要setState()必须放在一个if条件中 |
如果要发起网络请求,一般是在挂载阶段的componentDidMount()里面执行,因为在更新阶段要有执行时机,才会触发componentDidUpdate()
3.卸载时(卸载阶段)
执行时机:组件从页面中消失
钩子函数 | 触发时机 | 作用 |
---|---|---|
componentWillUnmount | 组件卸载(从页面中消失) | 执行清理工作(比如:清理定时器,移除监听事件等) |
import React from 'react';
import ReactDOM from 'react-dom';
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
// 计数器
count: 0
}
}
render() {
return (
<div>
{
this.state.count > 5 ? <Line /> : <Counter count={this.state.count }/>
}
<button onClick={ this.handle}>+1</button>
</div>
)
}
// 处理count
handle = () => {
this.setState({
count: this.state.count+1
})
}
}
// count>5之后,就显示Line组件
class Line extends React.Component {
render() {
return (
<div>
<p>豆豆上天了</p>
</div>
)
}
}
// count<=5时,显示的是Counter组件
class Counter extends React.Component {
// 故而在阶段
componentDidMount() {
this.timer = setInterval(() => {
console.log("计数器正在执行中");
},1000)
}
render() {
return (
<div>
<p>豆豆被打的次数:{ this.props.count}</p>
</div>
)
}
// 卸载阶段
// 这个组件不存在了,或者被其他组件替换了,困就触发这个事件
componentWillUnmount() {
console.log('豆豆上天了,Counter组件被Line组件替换了,不存在了');
clearInterval(this.timer)
}
}
ReactDOM.render(
<div><App /></div>,
document.getElementById('root')
);
React路由
SPA:单页面程序,就是只有一个html页面的应用程序;用户体验更好、对服务器的压力更小,所以更受欢迎。一个组件就是页面
前端路由的功能:让用户从一个视图(页面)导航到另一个视图(页面)
前端路由式一套映射规则,在React中,是URL路径与组件的对应关系
使用React路由就是配置路径和组件(配对)
使用步骤:
1.安装:
yarn add react-router-dom
npm i react-router-dom
2.导入(按需导入)路由的三个核心组件:Router,Route,Link
import {BrowserRouter as Router,Route,Link } from "react-router-dom"
Router是BrowserRouter的别名,按需导入的别名用as实现,解构数据的别名用**😗*实现
3.使用Router组件标签包裹整个应用
4.使用Link组件作为导航菜单(路由入口),相当于一个a标签,要有to属性(类似a标签的href属性),
5.使用Route组件配置路由规则和要战士的组件(路由出口),要有path属性和component,
<Router>
//路由入口
<Link to="/One">到One组件页面去</Link>
//路由出口
<Route path='/One' component={One}></Route>
</Router>
import React from 'react';
import ReactDOM from 'react-dom';
// 按需导入
// BrowserRouter as Router:按需导入BrowserRouter,用as,给它取个别名Router
import { BrowserRouter as Router, Route, Link } from "react-router-dom"
// /One页面
const One = () => {
return (
<div>
我是第一个页面
</div>
)
}
// App页面
class App extends React.Component {
render() {
return (
<Router>
{/* 路由入口 */}
<Link to="/One">到One组件页面去</Link>
{/* 路由出口 */}
<Route path='/One' component={One}></Route>
</Router>
)
}
}
ReactDOM.render(
<div><App /></div>,
document.getElementById('root')
);
常用组件说明
Router组件:包裹整个应用,一个React应用只需要使用一次
两种常用Router:HashRouter和BrowserRouter
HashRouter:使用URL的哈希值实现(localhost/#/One)
(推荐)BrowserRouter:使用H5的historyAPI实现(localhost:3000/One)
匹配模式
import React from 'react';
import ReactDOM from 'react-dom';
// 按需导入
// BrowserRouter as Router:按需导入BrowserRouter,用as,给它取个别名Router
import { BrowserRouter as Router, Route, Link } from "react-router-dom"
// Login页面
class Login extends React.Component {
handleLogin = () => {
// 编程式导航
// 跳转到某个页面
this.props.history.push('/home')
}
render() {
return (
<div>
我是Login页面
<button onClick={this.handleLogin}>Home</button>
</div>
)
}
}
// Home页面
// 因为是函数组件,所有没有自己的状态
const Home = props => {
const handleBack = () => {
// 编程式导航
// 退回上一页
props.history.go(-1)
}
return (
<div>
<h3>我是Home页面</h3>
<button onClick={handleBack}>Login</button>
</div>
)
}
// 默认显示的页面
const Hello = () => {
return (
<div>
<p>hello</p>
</div>
)
}
// App页面,整个应用界面
// 在Router标签里面,不要弄编程式导航,因为需要用到props,可是这个App是一级的,没有组件给它传值,所以编程式导航实现不了
class App extends React.Component {
render() {
return (
<Router>
<div>
{/* 路由入口 ,相当一个a标签*/}
{/*模糊匹配: to="/hello/login/home",原意是想去home页面,但是匹配的结果是:三个页面都匹配到了 */}
{/*精确匹配: 想要精确匹配到某个页面 ,就在该路由出口Route标签添加exact属性*/}
<Link to="/hello/login/home">登录</Link>
{/* 路由出口 */}
{/* 默认路由 */}
<Route exact path='/hello' component={Hello}></Route>
<Route exact path='/hello/login' component={Login}></Route>
<Route exact path='/hello/login/home' component={Home}></Route>
</div>
</Router>
)
}
}
ReactDOM.render(
<div><App /></div>,
document.getElementById('root')
);