全网react基础知识整理

488 阅读12分钟

这个是我最近在跟尚硅谷学习react,里面的一些文字都是老师的备注,我只是把学的东西和老师的资料,按照我自己比较喜欢的方法,整理了一下,方便自己在开发项目中能快速查看资料。

定义组件两种方法

分别是函数式组件和类式组件,两种方法

函数式组件实例

//1.创建函数式组件
function MyComponent(){
        console.log(this); //此处的this是undefined,因为babel编译后开启了严格模式
        return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
}
//2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
/* 
        执行了ReactDOM.render(<MyComponent/>.......之后,发生了什么?
        1.React解析组件标签,找到了MyComponent组件。
        2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中。
*/

类式组件实例

//1.创建类式组件
class MyComponent extends React.Component {
    render(){
        //render是放在哪里的?—— MyComponent的原型对象上,供实例使用。
        //render中的this是谁?—— MyComponent的实例对象 <=> MyComponent组件实例对象。
        console.log('render中的this:',this);
        return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>
    }
}
//2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
/* 
    执行了ReactDOM.render(<MyComponent/>.......之后,发生了什么?
    1.React解析组件标签,找到了MyComponent组件。
    2.发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法。
    3.将render返回的虚拟DOM转为真实DOM,随后呈现在页面中。
*/

组件实例三大属性

分别是state表示初始化状态、props、refs

state实例

//1.创建组件
class Weather extends React.Component{
//初始化状态
state = {isHot:false,wind:'微风'}
render(){
    const {isHot,wind} = this.state
    return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'},{wind}</h1>
}
//自定义方法————要用赋值语句的形式+箭头函数
changeWeather = ()=>{
    const isHot = this.state.isHot
    this.setState({isHot:!isHot})
}
//2.渲染组件到页面
ReactDOM.render(<Weather/>,document.getElementById('test'))
}

props实例

1props基本使用

//创建组件
class Person extends React.Component{
    render(){
        // console.log(this);
        const {name,age,sex} = this.props
        return (
                <ul>
                        <li>姓名:{name}</li>
                        <li>性别:{sex}</li>
                        <li>年龄:{age+1}</li>
                </ul>
        )
    }
}
//渲染组件到页面
ReactDOM.render(<Person name="jerry" age={19}  sex="男"/>,document.getElementById('test'))

2对props进行限制 通过一个propTypes的插件

//创建组件
class Person extends React.Component{
    render(){
        // console.log(this);
        const {name,age,sex} = this.props
        //props是只读的
        //this.props.name = 'jack' //此行代码会报错,因为props是只读的
        return (
                <ul>
                        <li>姓名:{name}</li>
                        <li>性别:{sex}</li>
                        <li>年龄:{age+1}</li>
                </ul>
        )
    }
}
//对标签属性进行类型、必要性的限制
Person.propTypes = {
    name:PropTypes.string.isRequired, //限制name必传,且为字符串
    sex:PropTypes.string,//限制sex为字符串
    age:PropTypes.number,//限制age为数值
    speak:PropTypes.func,//限制speak为函数
}
//指定默认标签属性值
Person.defaultProps = {
    sex:'男',//sex默认值为男
    age:18 //age默认值为18
}
//渲染组件到页面
ReactDOM.render(<Person name={100} speak={speak}/>,document.getElementById('test'))

function speak(){
    console.log('我说话了');
}

3props的简写方式

//创建组件
class Person extends React.Component{
    constructor(props){
        //构造器是否接收props,是否传递给super,取决于:是否希望在构造器中通过this访问props
        // console.log(props);
        super(props)
        console.log('constructor',this.props);
    }
    //对标签属性进行类型、必要性的限制
    static propTypes = {
        name:PropTypes.string.isRequired, //限制name必传,且为字符串
        sex:PropTypes.string,//限制sex为字符串
        age:PropTypes.number,//限制age为数值
    }
    //指定默认标签属性值
    static defaultProps = {
        sex:'男',//sex默认值为男
        age:18 //age默认值为18
    }
    render(){
        // console.log(this);
        const {name,age,sex} = this.props
        //props是只读的
        return (
            <ul>
                <li>姓名:{name}</li>
                <li>性别:{sex}</li>
                <li>年龄:{age+1}</li>
            </ul>
        )
    }
}

//渲染组件到页面
ReactDOM.render(<Person name="jerry"/>,document.getElementById('test1'))

4函数组件使用props

//创建组件
function Person (props){
    const {name,age,sex} = props
    return (
        <ul>
            <li>姓名:{name}</li>
            <li>性别:{sex}</li>
            <li>年龄:{age}</li>
        </ul>
    )
}
Person.propTypes = {
    name:PropTypes.string.isRequired, //限制name必传,且为字符串
    sex:PropTypes.string,//限制sex为字符串
    age:PropTypes.number,//限制age为数值
}

//指定默认标签属性值
Person.defaultProps = {
    sex:'男',//sex默认值为男
    age:18 //age默认值为18
}
//渲染组件到页面
ReactDOM.render(<Person name="jerry"/>,document.getElementById('test1'))

refs实例

1字符串形式的ref

//创建组件
class Demo extends React.Component{
    //展示左侧输入框的数据
    showData = ()=>{
        const {input1} = this.refs
        alert(input1.value)
    }
    //展示右侧输入框的数据
    showData2 = ()=>{
        const {input2} = this.refs
        alert(input2.value)
    }
    render(){
        return(
            <div>
                <input ref="input1" type="text" placeholder="点击按钮提示数据"/>&nbsp;
                <button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
                <input ref="input2" onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>
            </div>
        )
    }
}
//渲染组件到页面
ReactDOM.render(<Demo a="1" b="2"/>,document.getElementById('test'))

2回调函数形式

//创建组件
class Demo extends React.Component{
    //展示左侧输入框的数据
    showData = ()=>{
        const {input1} = this
        alert(input1.value)
    }
    //展示右侧输入框的数据
    showData2 = ()=>{
        const {input2} = this
        alert(input2.value)
    }
    render(){
        return(
            <div>
                <input ref={c => this.input1 = c } type="text" placeholder="点击按钮提示数据"/>&nbsp;
                <button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
                <input onBlur={this.showData2} ref={c => this.input2 = c } type="text" placeholder="失去焦点提示数据"/>&nbsp;
            </div>
        )
    }
}
//渲染组件到页面
ReactDOM.render(<Demo a="1" b="2"/>,document.getElementById('test'))

3回调ref中回调执行次数的问题

//创建组件
class Demo extends React.Component{

    state = {isHot:false}

    showInfo = ()=>{
        const {input1} = this
        alert(input1.value)
    }

    changeWeather = ()=>{
        //获取原来的状态
        const {isHot} = this.state
        //更新状态
        this.setState({isHot:!isHot})
    }

    saveInput = (c)=>{
        this.input1 = c;
        console.log('@',c);
    }

    render(){
        const {isHot} = this.state
        return(
            <div>
                    <h2>今天天气很{isHot ? '炎热':'凉爽'}</h2>
                    <input ref={this.saveInput} type="text"/><br/><br/>
                    <button onClick={this.showInfo}>点我提示输入的数据</button>
                    <button onClick={this.changeWeather}>点我切换天气</button>
            </div>
        )
    }
}
//渲染组件到页面
ReactDOM.render(<Demo/>,document.getElementById('test'))

生命周期分为三个阶段

初始化阶段

由ReactDOM.render()触发---初次渲染

  1. constructor()
  2. getDerivedStateFromProps()
  3. render()
  4. componentDidMount() =====> 常用 下面是解释为什么常用

一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息

更新阶段

由组件内部this.setSate()或父组件重新render触发

  1. getDerivedStateFromProps()
  2. shouldComponentUpdate()
  3. render() =====> 必须使用的一个
  4. getSnapshotBeforeUpdate()
  5. componentDidUpdate()

卸载组件

由ReactDOM.unmountComponentAtNode()触发

  1. componentWillUnmount() =====> 常用 下面是解释为什么常用

一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息

生命周期常用实例

componentDidMount

class Time extends React.Component {
    state = {date: new Date()}
    componentDidMount () {
        setInterval(() => {
            this.setState({
                    date: new Date()
            })
        }, 1000)
    }
    render () {
        return (
            <div>
                <h1>hello</h1>
                <input type="text"/>
                <span>
                        现在是:{this.state.date.toTimeString()}
                        <input type="text"/>
                </span>
            </div>
        )
    }
}

ReactDOM.render(<Time/>,document.getElementById('test'))

componentWillUnmount实例

class Count extends React.Component{
    //组件将要卸载的钩子
    componentWillUnmount(){
       console.log('Count---componentWillUnmount');
    }
    //卸载组件按钮的回调
    death = ()=>{
       ReactDOM.unmountComponentAtNode(document.getElementById('test'))
    }
    render(){
        return(
            <div>
                <button onClick={this.death}>卸载组件</button>
            </div>
        )
    }
}

鼠标事件

分别是onClick点击事件、onMouseEnter移入事件、onMouseLeave移出事件

render() {
    return (
        <li style={{backgroundColor: mouse?'#ddd': 'white'}} onMouseEnter={this.handleMouse(true)} onMouseLeave={this.handleMouse(false)} >
                <label>
                        <input type="checkbox" checked={done} onChange={this.handleCheck(id)} />
                        <span>{name}</span>
                </label>
                <button onClick={()=>this.handleDelete(id)} className="btn btn-danger" style={{display:mouse?'block': 'none'}}>删除</button>
        </li>
    )
}

键盘事件

分别是onKeyUp

render() {
    return (
        <div className="todo-header">
                <input onKeyUp={this.handleKeyUp} type="text" placeholder="请输入你的任务名称,按回车键确认"/>
        </div>
    )
}

input 事件

分别是onChange事件

    <input onKeyUp={this.handleKeyUp} type="text" placeholder="请输入你的任务名称,按回车键确认"/>
    <input type="checkbox" checked={done} onChange={this.handleCheck(id)} />

input 获取输入内容

refs回调函数形式

export default class Search extends Component {
  search = () =>{
   // 获取用户的输入
    // console.log(this.keyWordElement.value)
    // 常规赋值
    // const {value} = this.keyWordElement
    // 解构赋值连续
    const {keyWordElement: {value:keyWord}} = this

    render() {
        return (
                <section className="jumbotron">
                        <input ref={c=>this.keyWordElement = c} type="text" placeholder="输入关键词点击搜索"/>&nbsp;
                </section>
        )
    }
}

键盘形式 event形式

handleKeyUp = (event)=>{
    // 解构赋值获取keyCode,target
    const {keyCode,target} = event
    // 判断是否是回车按键
    if(keyCode !==13) return
    console.log(target.value)
    // 添加的todo名字不能空
    if(target.value.trim() === ''){
      alert('名字不能为空')
      return
    }
    const todoObj = {id: nanoid(),name: target.value,done: false}
    // 将todoObj给addTodo
    this.props.addTodo(todoObj)
    // 清空输入
    target.value = ''
}
<input onKeyUp={this.handleKeyUp} type="text" placeholder="请输入你的任务名称,按回车键确认"/>

checkbox形式用onChange方法

// 勾选,取消勾选某一个todo的回调 用的是高阶函数
  handleCheck = (id) => {
    return (event) => {
      this.props.updateTodo(id,event.target.checked)
    }
  }
render() {
const {done} = this.props
    return (
        <li>
            <label>
               <input type="checkbox" checked={done} onChange={this.handleCheck(id)} />

            </label>

        </li>
    )
}

父子之间通信

1.【父组件】给【子组件】传递数据:通过props传递
2.【子组件】给【父组件】传递数据:通过props传递,要求父提前给子传递一个函数

消息订阅与发布机制

是通过一个插件 pubsub-js

1.先订阅,再发布(理解:有一种隔空对话的感觉)
2.适用于任意组件间通信
3.要在组件的componentWillUnmount中取消订阅

兄弟组件通信案例

a组件

import React, { Component } from 'react'
import PubSub from 'pubsub-js'
import './index.css'

export default class List extends Component {
  // 初始化状态
  state = {
    users:[],
    isFirst: true,
    isLoading: false,
    err: ''
  }
  // 初使化
  componentDidMount(){
    this.token = PubSub.subscribe('atguigu',(msg,stateObj) =>{
      console.log('收到数据',stateObj)
      this.setState(stateObj)
    })
  }
  componentWillUnmount(){
    PubSub.unsubscribe(this.token)
  }
}

b组件 点击搜索的时候要发送订阅

import React, { Component } from 'react'
import PubSub from 'pubsub-js'
import axios from 'axios'

export default class Search extends Component {
  search = () =>{
    // 发送请求前通知List更新状态
    PubSub.publish('atguigu',{isFirst: false,isLoading: true})
    // console.log(keyWord)
    
  }
    render() {
        return (
            <section className="jumbotron">
                <h3 className="jumbotron-heading">搜索github用户</h3>
                <div>
                    <button onClick={this.search}>搜索</button>
                </div>
            </section>
        )
    }
}

路由

路由的基本使用

1.明确好界面中的导航区、展示区
2.导航区的a标签改为Link标签
	<Link to="/xxxxx">Demo</Link>
3.展示区写Route标签进行路径的匹配
	<Route path='/xxxx' component={Demo}/>
4.<App>的最外侧包裹了一个<BrowserRouter><HashRouter>

路由组件与一般组件

1.写法不同:
        一般组件:<Demo/>
        路由组件:<Route path="/demo" component={Demo}/>
2.存放位置不同:
        一般组件:components
        路由组件:pages
3.接收到的props不同:
         一般组件:写组件标签时传递了什么,就能收到什么
         路由组件:接收到三个固定的属性
         history:
                go: ƒ go(n)
                goBack: ƒ goBack()
                goForward: ƒ goForward()
                push: ƒ push(path, state)
                replace: ƒ replace(path, state)
        location:
                pathname: "/about"
                search: ""
                state: undefined
        match:
                params: {}
                path: "/about"
                url: "/about"

NavLink与封装NavLink

这里要安装一个插件 react-router-dom

npm i react-router-dom

1.NavLink可以实现路由链接的高亮,通过activeClassName指定样式名

NavLink实例

<NavLink activeClassName="atguigu" className="list-group-item" to="/about">About</NavLink>
<NavLink activeClassName="atguigu" className="list-group-item" to="/home">Home</NavLink>

封装NavLink实例

import React, { Component } from 'react'
import {Route} from 'react-router-dom'
import Home from './pages/Home' //Home是路由组件
import About from './pages/About' //About是路由组件
import Header from './components/Header' //Header是一般组件
import MyNavLink from './components/MyNavLink'

export default class App extends Component {
    render() {
        return (
        <div>
            <div className="row">
                <div className="col-xs-offset-2 col-xs-8">
                    <Header/>
                </div>
            </div>
            <div className="row">
                <div className="col-xs-2 col-xs-offset-2">
                    <div className="list-group">
                        {/* 在React中靠路由链接实现切换组件--编写路由链接 */}
                        <MyNavLink to="/about">About</MyNavLink>
                        <MyNavLink to="/home">Home</MyNavLink>
                    </div>
            </div>
            <div className="col-xs-6">
                <div className="panel">
                    <div className="panel-body">
                        {/* 注册路由 */}
                        <Route path="/about" component={About}/>
                        <Route path="/home" component={Home}/>
                    </div>
                </div>
                </div>
            </div>
        </div>
        )
    }
}

MyNavLink组件

import React, { Component } from 'react'
import {NavLink} from 'react-router-dom'

export default class MyNavLink extends Component {
    render() {
        // console.log(this.props);
        return (
            <NavLink activeClassName="atguigu" className="list-group-item" {...this.props}/>
        )
    }
}

Switch的使用

1.通常情况下,path和component是一一对应的关系。
2.Switch可以提高路由匹配效率(单一匹配)。 3.'react-router-dom'新增了一个Switch方法

switch实例

import React, { Component } from 'react'
import {Route,Switch} from 'react-router-dom'
import Home from './pages/Home' //Home是路由组件
import About from './pages/About' //About是路由组件
import Header from './components/Header' //Header是一般组件
import MyNavLink from './components/MyNavLink'
import Test from './pages/Test'

export default class App extends Component {
    render() {
        return (
            <div>
                <div className="row">
                        <div className="col-xs-offset-2 col-xs-8">
                                <Header/>
                        </div>
                </div>
                <div className="row">
                    <div className="col-xs-2 col-xs-offset-2">
                        <div className="list-group">
                            {/* 在React中靠路由链接实现切换组件--编写路由链接 */}
                            <MyNavLink to="/about">About</MyNavLink>
                            <MyNavLink to="/home">Home</MyNavLink>
                        </div>
                    </div>
                    <div className="col-xs-6">
                        <div className="panel">
                            <div className="panel-body">
                                {/* 注册路由 */}
                                <Switch>
                                    <Route path="/about" component={About}/>
                                    <Route path="/home" component={Home}/>
                                    <Route path="/home" component={Test}/>
                                </Switch>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        )
    }
}

解决多级路径刷新页面样式丢失的问题

1.public/index.html 中 引入样式时不写 ./ 写 / (常用)
2.public/index.html 中 引入样式时不写 ./ 写 %PUBLIC_URL% (常用)
3.使用HashRouter

路由的严格匹配与模糊匹配

1.默认使用的是模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序要一致)<br>
2.开启严格匹配:<Route exact={true} path="/about" component={About}/><br>
3.严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由

Redirect的使用

1.一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到Redirect指定的路由
2.具体编码:
<Switch>
    <Route path="/about" component={About}/>
    <Route path="/home" component={Home}/>
    <Redirect to="/about"/>
</Switch>

嵌套路由

1.注册子路由时要写上父路由的path值
2.路由的匹配是按照注册路由的顺序进行的

import React, { Component } from 'react'
import MyNavLink from '../../components/MyNavLink'
import {Route,Switch,Redirect} from 'react-router-dom'
import News from './News'
import Message from './Message'

export default class Home extends Component {
    render() {
        return (
            <div>
                <h3>我是Home的内容</h3>
                <div>
                    <ul className="nav nav-tabs">
                        <li>
                            <MyNavLink to="/home/news">News</MyNavLink>
                        </li>
                        <li>
                            <MyNavLink to="/home/message">Message</MyNavLink>
                        </li>
                    </ul>
                    {/* 注册路由 */}
                    <Switch>
                        <Route path="/home/news" component={News}/>
                        <Route path="/home/message" component={Message}/>
                        <Redirect to="/home/news"/>
                    </Switch>
                </div>
            </div>
        )
    }
}

向路由组件传递参数

1.params参数
    路由链接(携带参数):<Link to='/demo/test/tom/18'}>详情</Link>
    注册路由(声明接收):<Route path="/demo/test/:name/:age" component={Test}/>
    接收参数:this.props.match.params
2.search参数
    路由链接(携带参数):<Link to='/demo/test?name=tom&age=18'}>详情</Link>
    注册路由(无需声明,正常注册即可):<Route path="/demo/test" component={Test}/>
    接收参数:this.props.location.search
    备注:获取到的search是urlencoded编码字符串,需要借助querystring解析
3.state参数
    路由链接(携带参数):<Link to={{pathname:'/demo/test',state:{name:'tom',age:18}}}>详情</Link>
    注册路由(无需声明,正常注册即可):<Route path="/demo/test" component={Test}/>
    接收参数:this.props.location.state
    备注:刷新也可以保留住参数

params参数实例

父路由组件

import React, { Component } from 'react'
import {Link,Route} from 'react-router-dom'
import Detail from './Detail'

export default class Message extends Component {
    state = {
        messageArr:[
            {id:'01',title:'消息1'},
            {id:'02',title:'消息2'},
            {id:'03',title:'消息3'},
        ]
    }
    render() {
        const {messageArr} = this.state
        return (
            <div>
                <ul>
                        {
                            messageArr.map((msgObj)=>{
                                return (
                                    <li key={msgObj.id}>
                                        {/* 向路由组件传递params参数 */}
                                        <Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link>
                                    </li>
                                )
                            })
                        }
                </ul>
            <hr/>
            {/* 声明接收params参数 */}
            <Route path="/home/message/detail/:id/:title" component={Detail}/>
            </div>
        )
    }
}

子路由组件

import React, { Component } from 'react'

const DetailData = [
    {id:'01',content:'你好,中国'},
    {id:'02',content:'你好,尚硅谷'},
    {id:'03',content:'你好,未来的自己'}
]
export default class Detail extends Component {
    render() {
        console.log(this.props);
        // 接收params参数
        const {id,title} = this.props.match.params
        const findResult = DetailData.find((detailObj)=>{
                return detailObj.id === id
        })
        return (
            <ul>
                <li>ID:{id}</li>
                <li>TITLE:{title}</li>
                <li>CONTENT:{findResult.content}</li>
            </ul>
        )
    }
}

2.search参数

父路由组件

import React, { Component } from 'react'
import {Link,Route} from 'react-router-dom'
import Detail from './Detail'

export default class Message extends Component {
    state = {
        messageArr:[
            {id:'01',title:'消息1'},
            {id:'02',title:'消息2'},
            {id:'03',title:'消息3'},
        ]
    }
    render() {
        const {messageArr} = this.state
        return (
            <div>
                <ul>
                        {
                            messageArr.map((msgObj)=>{
                                return (
                                    <li key={msgObj.id}>
                                        {/* 向路由组件传递search参数 */}
                                        <Link to={`/home/message/detail/?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link>
                                    </li>
                                )
                            })
                        }
                </ul>
            <hr/>
           {/* search参数无需声明接收,正常注册路由即可 */}
            <Route path="/home/message/detail" component={Detail}/>
            </div>
        )
    }
}

子路由组件

import React, { Component } from 'react'

const DetailData = [
    {id:'01',content:'你好,中国'},
    {id:'02',content:'你好,尚硅谷'},
    {id:'03',content:'你好,未来的自己'}
]
export default class Detail extends Component {
    render() {
        console.log(this.props);
        // 接收search参数
        const {search} = this.props.location
        const {id,title} = qs.parse(search.slice(1))
        const findResult = DetailData.find((detailObj)=>{
                return detailObj.id === id
        })
        return (
            <ul>
                <li>ID:{id}</li>
                <li>TITLE:{title}</li>
                <li>CONTENT:{findResult.content}</li>
            </ul>
        )
    }
}

3.state参数

父路由组件

import React, { Component } from 'react'
import {Link,Route} from 'react-router-dom'
import Detail from './Detail'

export default class Message extends Component {
    state = {
        messageArr:[
            {id:'01',title:'消息1'},
            {id:'02',title:'消息2'},
            {id:'03',title:'消息3'},
        ]
    }
    render() {
        const {messageArr} = this.state
        return (
            <div>
                <ul>
                        {
                            messageArr.map((msgObj)=>{
                                return (
                                    <li key={msgObj.id}>
                                        {/* 向路由组件传递search参数 */}
                                        <Link to={{pathname:'/home/message/detail',state:{id:msgObj.id,title:msgObj.title}}}>{msgObj.title}</Link>
                                    </li>
                                )
                            })
                        }
                </ul>
            <hr/>
           {/* state参数无需声明接收,正常注册路由即可 */}
            <Route path="/home/message/detail" component={Detail}/>
            </div>
        )
    }
}

子路由组件

import React, { Component } from 'react'

const DetailData = [
    {id:'01',content:'你好,中国'},
    {id:'02',content:'你好,尚硅谷'},
    {id:'03',content:'你好,未来的自己'}
]
export default class Detail extends Component {
    render() {
        console.log(this.props);
        // 接收search参数
        const {id,title} = this.props.location.state || {}
        const findResult = DetailData.find((detailObj)=>{
                return detailObj.id === id
        })
        return (
            <ul>
                <li>ID:{id}</li>
                <li>TITLE:{title}</li>
                <li>CONTENT:{findResult.content}</li>
            </ul>
        )
    }
}

push与replace模式

push: a-b-c 可以回到上一级
replace: a-b-c 回不到上一级 适用于登录后,不需要重新回到登页面

<MyNavLink replace to="/about">About</MyNavLink>
<MyNavLink replace to="/home">Home</MyNavLink>

编程式路由导航

借助this.prosp.history对象上的API对操作路由跳转、前进、后退
-this.prosp.history.push()
-this.prosp.history.replace()
-this.prosp.history.goBack()
-this.prosp.history.goForward()
-this.prosp.history.go()

replaceShow = (id,title)=>{
    //replace跳转+携带params参数
    //this.props.history.replace(`/home/message/detail/${id}/${title}`)

    //replace跳转+携带search参数
    // this.props.history.replace(`/home/message/detail?id=${id}&title=${title}`)

    //replace跳转+携带state参数
    this.props.history.replace(`/home/message/detail`,{id,title})
}

pushShow = (id,title)=>{
    //push跳转+携带params参数
    // this.props.history.push(`/home/message/detail/${id}/${title}`)

    //push跳转+携带search参数
    // this.props.history.push(`/home/message/detail?id=${id}&title=${title}`)

    //push跳转+携带state参数
    this.props.history.push(`/home/message/detail`,{id,title})

}

back = ()=>{
    this.props.history.goBack()
}

forward = ()=>{
    this.props.history.goForward()
}

go = ()=>{
    this.props.history.go(-2)
}
<li key={msgObj.id}>
    <button onClick={()=> this.pushShow(msgObj.id,msgObj.title)}>push查看</button>
    <button onClick={()=> this.replaceShow(msgObj.id,msgObj.title)}>replace查看</button>
    <button onClick={this.back}>回退</button>&nbsp;
    <button onClick={this.forward}>前进</button>&nbsp;
    <button onClick={this.go}>go</button>
</li>

withRouter的使用

//withRouter可以加工一般组件,让一般组件具备路由组件所特有的API
//withRouter的返回值是一个新组件

import React, { Component } from 'react'
import {withRouter} from 'react-router-dom'

class Header extends Component {
    back = ()=>{
        this.props.history.goBack()
    }

    forward = ()=>{
        this.props.history.goForward()
    }

    go = ()=>{
        this.props.history.go(-2)
    }

    render() {
        console.log('Header组件收到的props是',this.props);
        return (
            <div className="page-header">
                <h2>React Router Demo</h2>
                <button onClick={this.back}>回退</button>&nbsp;
                <button onClick={this.forward}>前进</button>&nbsp;
                <button onClick={this.go}>go</button>
            </div>
        )
    }
}

export default withRouter(Header)

BrowserRouter与HashRouter的区别

1.底层原理不一样:
BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。
HashRouter使用的是URL的哈希值。

2.path表现形式不一样
BrowserRouter的路径中没有#,例如:localhost:3000/demo/test
HashRouter的路径包含#,例如:localhost:3000/#/demo/test

3.刷新后对路由state参数的影响
(1).BrowserRouter没有任何影响,因为state保存在history对象中。
(2).HashRouter刷新后会导致路由state参数的丢失!!!

4.备注:HashRouter可以用于解决一些路径错误相关的问题。