react基础复习

318 阅读16分钟

react基础

用react脚手架创建项目

npx create-react-app my-app

然后打开app.js文件,删除多余部分

function App() {
  return (
    <div >
          <h1>hello react</h1>
    </div>
  );
}

export default App;

JSX

在学习如何操作之前,先要了解一下JSX语法,想app.js里面像JavaScript又像html一样的语法就是JSX,上面写法的一些基本规则

1,有且只有一个根元素

2,class要用className

3,style的格式为style={{属性:值}},属性要用小驼峰命名

4,可以插入值,用{}


const No = {
  name: '不忘',
  age: '19'
}

function App() {
  return (
    <div >
      <h1>hello react</h1>
      <p>{No.name}有很多老婆,今年{No.age}了</p>
      <ul>
    </div>
  );
}

export default App;

5,也可以加入一些表达式


const No = {
  name: '不忘',
  age: '19',
  girlfriend:['冰冰','嘉嘉','琳琳']
}

function App() {
  return (
    <div >
      <h1>hello react</h1>
      <p>{No.name}有很多老婆,今年{No.age}了</p>
      <ul>
        <li>女朋友有:</li>
        {
          No.girlfriend.map((value,index)=>{
            return <li key={index}>{value}</li>
          })
        }
      </ul>
        {
          No.age>18? <h1>准备结婚了</h1> : <h1>等成年就结婚</h1>
        }
    </div>
  );
}

export default App;

6.标签小写开头会转换为html标签,大写开头会转换成组件

function App() {
  return (
    <div >
      <h1>hello react</h1>
        <Dream />
    </div>
  );
}

function Dream() {
  return (
    <h2>做人如果没用梦想,那和咸鱼有什么区别</h2>
  )
}
export default App;

组件

函数式组件

function App() {
  return (
    <div >
      <h1>hello react</h1>
    </div>
  );
}

类组件

class App extends React.Component{
  render() {
    return (
      <div>
        <h1>hello</h1>
      </div>
    );
  };
};

注意组件名首字母大写

组件实例三大属性

state

基础

class App extends React.Component{
  constructor(props) {
    super(props);
    this.state = {msg: '天地玄黄,宇宙洪荒。日月盈昃,辰宿列张。'}
  }
  render() {
    return (
      <div>
        <h1>hello</h1>
        <h2>{this.state.msg}</h2>
      </div>
    );
  };
};

优化一点

class App extends React.Component{
  constructor(props) {
    super(props);
    this.state = {msg: '天地玄黄,宇宙洪荒。日月盈昃,辰宿列张。'}
  }
  render() {
    const {msg} = this.state;
    return (
      <div>
        <h1>hello</h1>
        <h2>{msg}</h2>
      </div>
    );
  };
};

修改state

涉及事件调用,看到目录点击事件

class App extends React.Component{
  constructor(props) {
    super(props);
    this.state = {msg: '天地玄黄,宇宙洪荒。日月盈昃,辰宿列张。',show: true}
    this.change=this.change.bind(this)
  }
  render() {
    const {msg,show} = this.state;
    return (
      <div>
        <h1>hello</h1>
        <button onClick={this.change}>{!show? '出现':'消失'}</button>
        <h2>{show? msg :''}</h2>
      </div>
    );
  };

  change(){
    this.setState({show: !this.state.show});//知道修改state要调用setState就好了
  }
};

优化

class App extends React.Component{
  
  state = {msg: '天地玄黄,宇宙洪荒。日月盈昃,辰宿列张。',show: true}
  
  render() {
    const {msg,show} = this.state;
    return (
      <div>
        <h1>hello</h1>
        <button onClick={this.change}>{!show? '出现':'消失'}</button>
        <h2>{show? msg :''}</h2>
      </div>
    );
  };

  change=() => {
    this.setState({show: !this.state.show});//知道修改state要调用setState就好了
  }
};

props

基础用法

class App extends React.Component{
  render() {
    return (
      <div >
        <Dream name="不忘" girlfriend="冰冰"/>
        <br/>
        <br/>
        <Dream name="回忆" girlfriend="翠花"/>
    </div>
    );
  };
};

class Dream extends React.Component{
  render() {
    return (
      <div>
        <h1>hello {this.props.name}</h1>
        <h2>你的老婆{this.props.girlfriend}来了</h2>
      </div>
    );
  };
};

优化

class App extends React.Component{
  render() {
    const no={name: "不忘",girlfriend:'冰冰'}
    const yes={name: "回忆", girlfriend:'翠花'}
    return (
      <div >
        <Dream {...no}/>
        <br/>
        <br/>
        <Dream {...yes}/>
    </div>
    );
  };
};

class Dream extends React.Component{
  render() {
    const {name,girlfriend}=this.props;
    return (
      <div>
        <h1>hello {name}</h1>
        <h2>你的老婆{girlfriend}来了</h2>
      </div>
    );
  };
};

props限制

react@15.5版本以上

下包

npm i prop-types --save

导入

import PropTypes from 'prop-types';

使用

class App extends React.Component{
  render() {
    const no={name: "不忘",girlfriend:'冰冰'}
    const yes={name: "回忆", girlfriend:'翠花'}
    return (
      <div >
        <Dream {...no}/>
        <br/>
        <br/>
        <Dream {...yes}/>
    </div>
    );
  };
};

function Dream(props) {
    const {name,girlfriend,msg}=props;
    return (
      <div>
        <h1>hello {name}</h1>
        <h2>你的老婆{girlfriend}来了</h2>
        <p>{msg}</p>
      </div>
    );
};

Dream.propTypes={
  name: PropTypes.string,
}

Dream.defaultProps={
  msg:'开心的跳了起来'
}

优化

class App extends React.Component{
  render() {
    const no={name: "不忘",girlfriend:'冰冰'}
    const yes={name: "回忆", girlfriend:'翠花'}
    return (
      <div >
        <Dream {...no}/>
        <br/>
        <br/>
        <Dream {...yes}/>
    </div>
    );
  };
};

class Dream extends React.Component{
  static propTypes = {//限制类型
    name: PropTypes.string,
  }

  static defaultProps = {//设置默认值
    msg:'开心的跳了起来'
  }
  render() {
    const {name,girlfriend,msg}=this.props;
    return (
      <div>
        <h1>hello {name}</h1>
        <h2>你的老婆{girlfriend}来了</h2>
        <p>{msg}</p>
      </div>
    );
  };
};

常用的限制有

数组:array

布尔:bool

函数:func

数字:number

对象:object

字符串:string

函数式组件的props

class App extends React.Component{
  render() {
    const no={name: "不忘",girlfriend:'冰冰'}
    const yes={name: "回忆", girlfriend:'翠花'}
    return (
      <div >
        <Dream {...no}/>
        <br/>
        <br/>
        <Dream {...yes}/>
    </div>
    );
  };
};

function Dream(props) {
    const {name,girlfriend,msg}=props;
    return (
      <div>
        <h1>hello {name}</h1>
        <h2>你的老婆{girlfriend}来了</h2>
        <p>{msg}</p>
      </div>
    );
};

refs

能少用就少用

字符串ref(最好不用)


class App extends React.Component{
  showData=()=>{
    const {data} = this.refs;
    console.log(data.value);
  }
  render() {
    return (
      <div >
       <input onBlur={this.showData} type="text" ref="data" name="" id="" placeholder="输入查询内容"/>
      </div>
    );
  };
};

回调函数ref

class App extends React.Component{
  showData=()=>{
    const {data} = this;
    console.log(data.value);
  }
  render() {
    return (
      <div >
       <input onBlur={this.showData} type="text" ref={value=>this.data=value} name="" id="" placeholder="输入查询内容"/>
      </div>
    );
  };
};

类绑定


class App extends React.Component{
  showData=()=>{
     const {data}=this;
    console.log(data.value);
  }
  saveInput=(value)=>{
    this.data=value;
  }
  render() {
    return (
      <div >
       <input onBlur={this.showData} type="text" ref={this.saveInput} name="" id="" placeholder="输入查询内容"/>
      </div>
    );
  };
};

cractRef(推荐)

class App extends React.Component{
  saveRef=React.createRef()
  showData=()=>{
    console.log(this.saveRef.current.value);
  }
  render() {
    return (
      <div >
       <input onBlur={this.showData} type="text" ref={this.saveRef} name="" id="" placeholder="输入查询内容"/>
      </div>
    );
  };
};

事件

事件名用onXxxx,on后面第一个字母大写

委托的方式挂载在外最外层元素上

可以通过event.target获取发生对象的dom

class App extends React.Component{
  showData=(event) =>{
    console.log(event.target.value);
  }
  render() {
    return (
      <div >
       <input onBlur={this.showData} type="text" name="" id="" placeholder="输入查询内容"/>
      </div>
    );
  };
};

获取参数

在获取参数前先了解一个概念叫函数柯里化

调用的函数参数在返回发函数中统一处理的方式叫函数柯里化

class App extends React.Component{
  state={msg:'',find:''}
  showData=(data) =>{
    return (event)=> {
      this.setState({[data]:event.target.value});
      this.setState({msg:data});
    }
  }
  render() {
    const {msg,find} = this.state
    return (
      <div >
       <input onChange={this.showData('find')} type="text" name="" id="" placeholder="输入查询内容"/>
       <h1>{msg}</h1>
       <p>{find}</p>
      </div>
    );
  };
};

不用柯里化

class App extends React.Component{
  state={msg:'',find:''}
  showData=(data,event) =>{
      this.setState({[data]:event.target.value});
      this.setState({msg:data});
    }
  render() {
    const {msg,find} = this.state
    return (
      <div >
       <input onChange={(event)=>this.showData('find',event)} type="text" name="" id="" placeholder="输入查询内容"/>
       <h1>{msg}</h1>
       <p>{find}</p>
      </div>
    );
  };
};

受控组件,非受控组件

简单讲就是有木有state保存数据

非受控组件

class App extends React.Component{
  showData=(event) =>{
    console.log(event.target.value);
  }
  render() {
    return (
      <div >
       <input onBlur={this.showData} type="text" name="" id="" placeholder="输入查询内容"/>
      </div>
    );
  };
};

受控组件

class App extends React.Component{
  state={msg:''}
  showData=(event) =>{
    this.setState({msg:event.target.value});
  }
  render() {
    const {msg} = this.state
    return (
      <div >
       <input onChange={this.showData} type="text" name="" id="" placeholder="输入查询内容"/>
       <h1>{msg}</h1>
      </div>
    );
  };
};

组件生命周期

简单演示新的生命周期函数,有三种情况

初始化阶段

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class App extends Component{
  constructor(props) {
    super(props);
    this.state = {msg: '天地玄黄,宇宙洪荒。日月盈昃,辰宿列张。'}
  }
  render() {
    return (
      <div>
        <h1>hello</h1>
        <div id="children">
          <Children msg={this.state.msg}/>
        </div>
      </div>
    );
  };
};

class Children extends Component{
  constructor(props) {
    console.log('constructor');
    super(props);
    this.state={}
  }
  static getDerivedStateFromProps(props,state) {
    console.log('getDerivedStateFromProps',props,state);
      return props //将state的值转换成接受的props,返回unll不发送改变
  }

  componentDidMount(){
    console.log('componentDidMount');//一般写组件一开始就出现的功能,网络加载,定时器等等
  }
  render() {
    console.log('render');
    return (
      <div>
        <h2>{this.state.msg}</h2>
        <h2>{this.props.msg}</h2>
      </div>
    );
  };
};


export default App;

更新阶段

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class App extends Component{
  constructor(props) {
    super(props);
    this.state = {msg: '天地玄黄,宇宙洪荒。日月盈昃,辰宿列张。'}
  }
  render() {
    return (
      <div>
        <h1>hello</h1>
        <div id="children">
          <Children msg={this.state.msg}/>
        </div>
      </div>
    );
  };
};

class Children extends Component{
  state={mage:''}

  static getDerivedStateFromProps(props,state){
    console.log('getDerivedStateFromProps',props,state);
      return null 
  }

  shouldComponentUpdate(){
    console.log('shouldComponentUpdate');
    return true
    //返回true更新,返回false不更新
  }

  getSnapshotBeforeUpdate(preProps,preState){
    console.log('getSnapshotBeforeUpdate',preProps,preState);
    return '来自getSnapshotBeforeUpdate的值'
    //即将更新,可以获取更新前的数据,返回的值都会被componentDidUpdate接收
  }

  componentDidUpdate(preProps,preState,val){
    console.log('componentDidUpdate',preProps,preState,val);
    //变化前的props,state和来自getSnapshotBeforeUpdate的返回值
  }

  change=()=> {
    this.setState({msge:'寒来暑往,秋收冬藏。闰余成岁,律吕调阳'})
  }
  render() {
    console.log('render');
    return (
      <div>
        <h2>{this.props.msg}</h2>
        <h2>{this.state.msge}</h2>
        <button onClick={this.change}>改变</button>
      </div>
    );
  };
};


export default App;

卸载组件

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class App extends Component{
state = {msg: '天地玄黄,宇宙洪荒。日月盈昃,辰宿列张。'}

componentWillMount() {
  console.log('componentWillMount');//卸载前执行
}

  remove=() => {
    ReactDOM.unmountComponentAtNode(document.getElementById('root'));//卸载组件
  }
  render() {
    return (
      <div>
        <h1>hello</h1>
        <h2>{this.state.msg}</h2>

        <button onClick={this.remove}>卸载</button>
      </div>
    );
  };
};


export default App;

diffing算法

diffing算法是用来对比虚拟dom和真实dom的,当虚拟dom发生改变时,将真实dom不同的部分进行修改

虚拟dom中key是虚拟dom的唯一标识,对比key相同的元素,如果不同改变真实dom的内容,如果真实dom没用虚拟dom的key的元素,就会创建对应的元素

当用index作为key时,逆序的添加删除等破坏顺序的操作会影响效率

如果包含输入内的dom就会发生错误


class App extends React.Component{
  state={data:[
    {name:'不忘',age:20},
    {name:'回忆',age:18},
  ]}

  add=()=>{
    this.setState({data: [
      {name:'留念',age:19},
      {name:'不忘',age:20},
      {name:'回忆',age:18},
    ]})
  }
  render() {
    const {data} = this.state
    return (
      <div >
        <button onClick={this.add}>添加</button>
      {
        data.map(val=>{
          return (
            <h1>{val.name}牛逼,今年{val.age} <input type="text"/></h1>
          )
        })
      }
      </div>
    );
  };
};
//当在输入框内输入内容然后点击增加,就会发现输入框和前面的值不对应了

代理服务器配置

在src下创建setupProxy.js

const { proxy } = require('http-proxy-middleware')
// 或
// const proxy = require('http-proxy-middleware')

module.exports = function (app) {
  app.use(proxy('/api', {
    target: 'http://172.16.136.249:8080',
    secure: false,
    changeOrigin: true,
    pathRewrite: {
      "^/api": "/api"
    }
  }))
}

路由(dom)

基础使用

下载包

npm i react-router-dom

打开index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {BrowserRouter} from 'react-router-dom';
//导入BrowserRouter,作为组件嵌套App组件,使用history模式,所有还有HasRouter对应hash模式
ReactDOM.render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

打开App.js

import React from 'react';
import {Link,Route} from 'react-router-dom';
import {no} from './index/no.jsx'
import {yes} from './index/yes'

class App extends React.Component{
  render() {
    return (
      <div >
        <Link to="/no" >不忘的老婆</Link>
         {/*Link组件设置点击跳转的路径,to是跳转的路径*/}
        <br/>
        <Link to="/yes" >回忆的老婆</Link>
       <br/>
          <Route path="/no" component={no}/>
           {/*Route设置跳转的内容,path是跳转的路径,component是跳转的内容*/}
          <Route path="/yes" component={yes}/>
       
      </div>
    );
  };
};


export default App;

创建index文件夹,创建no.jsx和yes.jsx

no.jsx


import React from 'react';

export class no extends React.Component{
  render() {
    return (
      <div>
        <h1>冰冰</h1>
      </div>
    );
  };
};

yes.jsx


import * as React from 'react';

export class yes extends React.Component{
  render() {
    return (
      <div>
        <h1>如花</h1>
      </div>
    );
  };
};

路由组件

像上面那样的no.jsx和yes.jsx就属于路由组件

看看路由组件的props的有固定的属性

history

localtion

matchactive

NavLink

前面有用过Link组件,和他的区别就是,当你点击是会自动添加一个active的类名,如果你自定义了一个类名activeClassName,也会在点击时自动添加上,点击别的NavLink是自动清除

修改App.js

import React from 'react';
import './App.css'
import {NavLink,Route} from 'react-router-dom';
import {no} from './index/no.jsx'
import {yes} from './index/yes'

class App extends React.Component{
  render() {
    return (
      <div >
        <NavLink activeClassName="ch" to="/no" >不忘的老婆</NavLink>
        <br/>
        <NavLink activeClassName="ch"  to="/yes" >回忆的老婆</NavLink>
       <br/>
          <Route path="/no" component={no}/>
          <Route path="/yes" component={yes}/>
       
      </div>
    );
  };
};


export default App;

修改App.css

.active{
    color: red;
}

.ch{
    font-size: 30px;
}

Switch

当匹配到对应路由后就不会再向下匹配

修改App.js

import React from 'react';
import './App.css'
import {NavLink,Route,Switch} from 'react-router-dom';
import {no} from './index/no.jsx'
import {yes} from './index/yes'

class App extends React.Component{
  render() {
    return (
      <div >
        <NavLink activeClassName="ch" to="/no" >不忘的老婆</NavLink>
        <br/>
        <NavLink activeClassName="ch"  to="/yes" >回忆的老婆</NavLink>
       <br/>
       <Switch>
          <Route path="/no" component={no}/>
          <Route path="/yes" component={yes}/>
       </Switch>
      </div>
    );
  };
};


export default App;

模糊匹配

就说当路由相同时,后面多几个子路由依然会获取当前路由的信息

import React from 'react';
import './App.css'
import {NavLink,Route,Switch} from 'react-router-dom';
import {no} from './index/no.jsx'
import {yes} from './index/yes'

class App extends React.Component{
  render() {
    return (
      <div >
        <NavLink activeClassName="ch" to="/no/suiyi" >不忘的老婆</NavLink>
        <br/>
        <NavLink activeClassName="ch"  to="/yes" >回忆的老婆</NavLink>
       <br/>
       <Switch>
          <Route path="/no" component={no}/>
          <Route path="/yes" component={yes}/>
       </Switch>
      </div>
    );
  };
};


export default App;

开启精准匹配,就需要路由一模一样的

import React from 'react';
import './App.css'
import {NavLink,Route,Switch} from 'react-router-dom';
import {no} from './index/no.jsx'
import {yes} from './index/yes'

class App extends React.Component{
  render() {
    return (
      <div >
        <NavLink activeClassName="ch" to="/no" >不忘的老婆</NavLink>
        <br/>
        <NavLink activeClassName="ch"  to="/yes" >回忆的老婆</NavLink>
       <br/>
       <Switch>
       	  <Route exact path="/no" component={no}/>
           {/*exact开启精准匹配*/}
          <Route path="/yes" component={yes}/>
       </Switch>
      </div>
    );
  };
};


export default App;

Redirect

当路由都匹配不上时就会去redirect

import React from 'react';
import './App.css'
import {NavLink,Route,Switch,Redirect} from 'react-router-dom';
import {no} from './index/no.jsx'
import {yes} from './index/yes'
import {noFind} from './index/nofind'

class App extends React.Component{
  render() {
    return (
      <div >
        <NavLink activeClassName="ch" to="/no" >不忘的老婆</NavLink>
        <br/>
        <NavLink activeClassName="ch"  to="/yes" >回忆的老婆</NavLink>
        <br/>
        <NavLink activeClassName="ch"  to="/nover" >你的老婆</NavLink>
       <br/>
       <Switch>
          <Route path="/no" component={no}/>
          <Route path="/yes" component={yes}/>
          <Route path="/notFind" component={noFind}/>
          <Redirect to="/notFind"/>
           {/*重定向到notFind路径*/}
       </Switch>
      </div>
    );
  };
};


export default App;

路由嵌套

打开no.jsx


import React from 'react';
import {Link,Route} from 'react-router-dom'
import binbin from './noGirl/binbin'
import jiajia from './noGirl/jiajia'

export class no extends React.Component{
  render() {
    return (
      <div>
        <Link to="/no/binbin" className="next">冰冰</Link>
        <Link to="/no/jiajia" className="next">嘉嘉</Link>
		{/*路径的名字要加上父路由的内容*/}
        <Route path="/no/binbin" component={binbin}/>
        <Route path="/no/jiajia" component={jiajia}/>
      </div>
    );
  };
};

创建noGirl文件夹,然后创建binbin.jsx和jiajia.jsx

binbin.jsx

import React, { Component } from 'react';

class binbin extends Component {
    render() {
        return (
            <div>
                <p>性格</p>
                <p>爱好</p>
                <p>照片</p>
            </div>
        );
    }
}

export default binbin;

jiajia.jsx

import React, { Component } from 'react';

class jiajia extends Component {
    render() {
        return (
            <div>
                <p>身高</p>
                <p>体重</p>
                <p>年龄</p>
            </div>
        );
    }
}

export default jiajia;

带参数路由

params传参

先看no.jsx


import React from 'react';
import {Link,Route} from 'react-router-dom'
import girl from './girl'

export class no extends React.Component{
  render() {
    return (
      <div>
        <Link to="/no/1" className="next">第一个老婆</Link>
        <Link to="/no/2" className="next">第二个老婆</Link>
            {/*在后面加入参数*/}
        <Route path="/no/:id" component={girl}/>
            {/*用/:参数/:参数的形式传递*/}
      </div>
    );
  };
};

然后修改girl.jsx

import React, { Component } from 'react';

class girl extends Component {
    state={girl:[
        {id:1,name: "冰冰"},
        {id:2,name: "嘉嘉"}
    ]}
    render() {
        let {girl}=this.state;
        const {id}=this.props.match.params;
        //在this.props.match.params接收
        const c=girl.find(value => {
            return value.id===id-0
        })
        return (
            <div>
               <h1>
                {c.name}
               </h1>
            </div>
        );
    }
}

export default girl;

serch传参

修改no.jsx


import React from 'react';
import {Link,Route} from 'react-router-dom'
import girl from './girl'

export class no extends React.Component{
  render() {
    return (
      <div>
        <Link to="/no/?id=1&msg=非常好看" className="next">第一个老婆</Link>
        <Link to="/no/?id=2&msg=非常可爱" className="next">第二个老婆</Link>
            {/*用?属性=值&属性=值的方式传递*/}
        <Route path="/no" component={girl}/>
      </div>
    );
  };
};

修改girl.jsx

import React, { Component } from 'react';
import qs from 'querystring'

class girl extends Component {
    state={girl:[
        {id:1,name: "冰冰"},
        {id:2,name: "嘉嘉"}
    ]}
    render() {
        let {girl}=this.state;
        const {search}=this.props.location;
        {/*接收参数*/}
        const data=qs.parse(search);
        {/*变成对象*/}
        const c=girl.find(value => {
            return value.id===data['?id']-0
        })
        console.log(data['?id']);
        return (
            <div>
               <h1>
                {c.name}
               </h1>
               <p>{data.msg}</p>
            </div>
        );
    }
}

export default girl;

state传参

修改no.jsx


import React from 'react';
import {Link,Route} from 'react-router-dom'
import girl from './girl'

export class no extends React.Component{
  render() {
    return (
      <div>
        <Link to={{pathname:'/no',state:{id:1,msg:'非常好看'}}} className="next">第一个老婆</Link>
        <Link to={{pathname:'/no',state:{id:2,msg:'非常可爱'}}} className="next">第二个老婆</Link>

        <Route path="/no" component={girl}/>
      </div>
    );
  };
};

修改girl.jsx

import React, { Component } from 'react';
import qs from 'querystring'

class girl extends Component {
    state={girl:[
        {id:1,name: "冰冰"},
        {id:2,name: "嘉嘉"}
    ]}
    render() {
        let {girl}=this.state;
        const {id,msg}=this.props.location.state || {};
        const c=girl.find(value => {
            return value.id===id-0
        }) || {}
        return (
            <div>
               <h1>
                {c.name}
               </h1>
               <p>{msg}</p>
            </div>
        );
    }
}

export default girl;

push和replace

push会留下历史记录,能够返回,replace没有记录,不能返回

打开app.js

import React from 'react';
import './App.css'
import {NavLink,Route,Switch,Redirect} from 'react-router-dom';
import {no} from './index/no.jsx'
import {yes} from './index/yes'
import {noFind} from './index/nofind'

class App extends React.Component{
  render() {
    return (
      <div >
        <NavLink replace activeClassName="ch" to="/no" >不忘的老婆</NavLink>
            {/*replace开启路由*/}
        <br/>
        <NavLink replace activeClassName="ch"  to="/yes" >回忆的老婆</NavLink>
        <br/>
        <NavLink replace activeClassName="ch"  to="/nover" >你的老婆</NavLink>
       <br/>
       <Switch>
          <Route path="/no" component={no}/>
          <Route path="/yes" component={yes}/>
          <Route path="/notFind" component={noFind}/>
          <Redirect to="/notFind"/>
       </Switch>
      </div>
    );
  };
};


export default App;

编程式导航

将路由跳转用函数打方式实现

打开no.jsx


import React from 'react';
import {Link,Route} from 'react-router-dom'
import girl from './girl'

export class no extends React.Component{
  go=() => {
  //   this.props.history.push('/no/1')
  //   this.props.history.push('/no/?id=1')
    this.props.history.push('/no/',{id:1,msg:'好看'})

    // this.props.history.replace('/no/1')
    // this.props.history.replace('/no/?id=1')
    // this.props.history.replace('/no/',{id:1})

    // this.props.history.goBack()//后退
    // this.props.history.goForward()//前进
    // this.props.history.go()//正数向前跳几页,负数向后跳几页
  }

  goer=() => {
    this.props.history.push('/no/',{id:2,msg:'可爱'})
  }

  render() {
    return (
      <div>
       <h1 onClick={this.go}>第一个老婆</h1>
       <h1 onClick={this.goer}>第二个老婆</h1>

        <Route path="/no" component={girl}/>
      </div>
    );
  };
};

weithRouter

一般组件没有办法使用编程式导航,这是可以用withRouter

打开App.js

import React from 'react';
import './App.css'
import {NavLink,Route,Switch,Redirect, withRouter} from 'react-router-dom';
import {no} from './index/no.jsx'
import {yes} from './index/yes'
import {noFind} from './index/nofind'

class App extends React.Component{
  render() {
    return (
      <div >
        <NavLink replace activeClassName="ch" to="/no" >不忘的老婆</NavLink>
        <br/>
        <NavLink replace activeClassName="ch"  to="/yes" >回忆的老婆</NavLink>
        <br/>
        <NavLink replace activeClassName="ch"  to="/nover" >你的老婆</NavLink>
       <br/>
       <Switch>
          <Route path="/no" component={no}/>
          <Route path="/yes" component={yes}/>
          <Route path="/notFind" component={noFind}/>
          <Redirect to="/notFind"/>
       </Switch>
      </div>
    );
  };
};


export default withRouter(App);
//这里导出时用withRouter生成新的组件,可以拥有路由组件的api

BrowserRouter和HashRouter的区别

底层原理,BrowserRouter使用h5的history,不兼容ie9以下,HashRouter使用哈希值

路径的样子不一样HashRouter的路由中间隔一个#

HashRouter使用state传参刷新后数据会丢失

redux

下包

npm i redux
npm i redux-thunk //支持异步

基本使用,异步也比较少用

修改app.js

import React, { Component } from 'react';
import store from './redux/store';
import {add,less,addAsync} from './redux/action';

class App extends Component {
  state={}
  add=()=>{
    store.dispatch(add(1))
  }

  less=()=>{
    store.dispatch(less(1))
  }

  addAsync=()=>{
    store.dispatch(addAsync(1,1000))
  }
componentDidMount() {
  
}
  render() {
    return (
      <div>
        和:{store.getState()}
        <ul>
          <li><button onClick={this.add}></button></li>
          <li><button onClick={this.less}></button></li>
          <li><button onClick={this.addAsync}>异步加</button></li>
        </ul>
        
      </div>
    );
  }
}

export default App;

修改index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

import store from './redux/store'


ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

store.subscribe(()=>{
  ReactDOM.render(
    <React.StrictMode>
      <App />
    </React.StrictMode>,
    document.getElementById('root')
  );
})

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

创建redux文件夹

在下面创建store.js

import {createStore,applyMiddleware} from 'redux'
import reducer from './reducer'
import thunk from 'redux-thunk'

export default createStore(reducer,applyMiddleware(thunk))

创建reducer.js

import {ADD,LESS} from './constant'

const firstState=0
export default function count(preState=firstState,action){
    const {type,data} = action;
    switch(type){
        case ADD:
            return preState+data
        case LESS:
            return preState-data
        default:
            return preState     
    }
}

创建action.js

import {ADD,LESS} from './constant'


export const add=data=>({type:ADD, data})
export const less=data=>({type:LESS, data})
export const addAsync=(data,time)=>{
    return (dispatch)=>{
        setTimeout(()=>{
            dispatch(add(data))
        },time)
    }
}

创建constant.js

export const ADD='add'
export const LESS='less'

react-redux

下包

npm i react-redux

修改app.js

import React, { Component } from 'react';
import Son from './son'

class App extends Component {
  render() {
    return (
      <div>
        <Son/>
      </div>
    );
  }
}

export default App;

创建son.js

import React, { Component } from 'react'
import {connect} from 'react-redux'
import {add,less,addAsync} from './redux/count_action'


class Son extends Component {
    state={}
    add=()=>{
      this.props.add(1)
    }
  
    less=()=>{
        this.props.less(1)
    }
  
    addAsync=()=>{
        this.props.addAsync(1,500)
    }

    render() {
      return (
        <div>
          和:{this.props.total}
          <ul>
            <li><button onClick={this.add}></button></li>
            <li><button onClick={this.less}></button></li>
            <li><button onClick={this.addAsync}>异步加</button></li>
          </ul>
          
        </div>
      );
    }
  }


export default connect(
    state=> ({total:state}),//传递状态
    {
        add,
        less,
        addAsync,
    } //操作状态的方法
    )(Son)

  

修改index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import store from './redux/store'
import {Provider} from 'react-redux'



ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);


// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

共享数据

创建otherSon.js

import React, { Component } from 'react'
import {connect} from 'react-redux'
import {add,less,addAsync} from './redux/action'


class otherSon extends Component {
    state={}
    add=()=>{
      this.props.add(1)
    }
  
    less=()=>{
        this.props.less(1)
    }
  
    addAsync=()=>{
        this.props.addAsync(1,500)
    }

    render() {
      return (
        <div>
          和:{this.props.all}
          <ul>
            <li><button onClick={this.add}></button></li>
            <li><button onClick={this.less}></button></li>
            <li><button onClick={this.addAsync}>异步加</button></li>
          </ul>
          
        </div>
      );
    }
  }

export default connect(
    state=> ({all:state.othercount}),//传递状态
    {
        add,
        less,
        addAsync,
    } //操作状态的方法
    )(otherSon)

  

共享值

在创建一个otherson.jsx

import React, { Component } from 'react'
import {connect} from 'react-redux'
import {adda,lessa,addAsynca} from './redux/action'


class otherSon extends Component {
    state={}
    adda=()=>{
      this.props.adda(1)
    }
  
    lessa=()=>{
        this.props.lessa(1)
    }
  
    addAsynca=()=>{
        this.props.addAsynca(1,500)
    }

    render() {
      return (
        <div>
          和:{this.props.all}
          <ul>
            <li><button onClick={this.adda}></button></li>
            <li><button onClick={this.lessa}></button></li>
            <li><button onClick={this.addAsynca}>异步加</button></li>
          </ul>
          
        </div>
      );
    }
  }


export default connect(
    state=> ({all:state.othercount}),//传递状态
    {
        adda,
        lessa,
        addAsynca,
    } //操作状态的方法
    )(otherSon)

  

修改son.jsx

import React, { Component } from 'react'
import {connect} from 'react-redux'
import {add,less,addAsync} from './redux/action'


class Son extends Component {
    state={}
    add=()=>{
      this.props.add(1)
    }
  
    less=()=>{
        this.props.less(1)
    }
  
    addAsync=()=>{
        this.props.addAsync(1,500)
    }

    render() {
      return (
        <div>
          和:{this.props.total}
          <ul>
            <li><button onClick={this.add}></button></li>
            <li><button onClick={this.less}></button></li>
            <li><button onClick={this.addAsync}>异步加</button></li>
          </ul>
          
        </div>
      );
    }
  }


export default connect(
    state=> ({total:state.count}),//传递状态
    {
        add,
        less,
        addAsync,
    } //操作状态的方法
    )(Son)

  

修改store.js

import {createStore,applyMiddleware,combineReducers} from 'redux'
import count from './reducer'
import othercount from './other_count'
import thunk from 'redux-thunk'

const all=combineReducers({
    count,
    othercount
})

export default createStore(all,applyMiddleware(thunk))

创建other_count.js

import {ADDA,LESSA} from './constant'


const firstState=0
export default function count(preState=firstState,action){
    const {type,data} = action;
    switch(type){
        case ADDA:
            return preState+data
        case LESSA:
            return preState-data
        default:
            return preState     
    }
}

修改action.js

import {ADD,LESS,ADDA,LESSA} from './constant'


export const add=data=>({type:ADD, data})
export const less=data=>({type:LESS, data})
export const addAsync=(data,time)=>{
    return (dispatch)=>{
        setTimeout(()=>{
            dispatch(add(data))
        },time)
    }
}

export const adda=data=>({type:ADDA, data})
export const lessa=data=>({type:LESSA, data})
export const addAsynca=(data,time)=>{
    return (dispatch)=>{
        setTimeout(()=>{
            dispatch(adda(data))
        },time)
    }
}

修改constant.js

export const ADD='add'
export const LESS='less'

export const ADDA='adda'
export const LESSA='lessa'

扩展

setState

两种写法

第一种,对象式

import React, { Component } from 'react';

class App extends Component {
  state={name:'冰冰'};
  change=()=>{
    this.setState({name:this.state.name+',嘉嘉'},()=>{
      console.log(this.state.name);//冰冰,嘉嘉
    });
    console.log(this.state.name);//冰冰
  }
  render() {
    return (
      <div>
        <h1>不忘的老婆是{this.state.name}</h1>
        <button onClick={this.change}>真实情况</button>
      </div>
    );
  }
}

export default App;

第二种,编程式

import React, { Component } from 'react';

class App extends Component {
  state={name:'冰冰'};
  change=()=>{
    this.setState(state=>({name:state.name+',嘉嘉'}),()=>{
      console.log(this.state.name);//冰冰,嘉嘉
    });
    console.log(this.state.name);//冰冰
  }
  render() {
    return (
      <div>
        <h1>不忘的老婆是{this.state.name}</h1>
        <button onClick={this.change}>真实情况</button>
      </div>
    );
  }
}

export default App;

lazy

懒加载,按需加载

import React,{lazy,Suspense} from 'react';
import './App.css'
import {NavLink,Route} from 'react-router-dom';

const no=lazy(()=> import('./index/no.jsx'));
const yes=lazy(()=> import('./index/yes.jsx'));

class App extends React.Component{
  render() {
    return (
      <div >
        <NavLink activeClassName="ch" to="/no" >不忘的老婆</NavLink>
        <br/>
        <NavLink activeClassName="ch"  to="/yes" >回忆的老婆</NavLink>
       <br/>
       <Suspense fallback={<h1>等待。。。。</h1>} >
          <Route path="/no" component={no}/>
          <Route path="/yes" component={yes}/>
       </Suspense>
         
       
      </div>
    );
  };
};


export default App;

hooks

state hooks

使函数组件能保存数据


import React, { Component } from 'react';

function App() {
  const [name,setNmae]=React.useState('冰冰')
  function change(){
    // setNmae(name+',嘉嘉');
    setNmae(name=>name+',嘉嘉');
  }
  return (
    <div>
      <h1>不忘的老婆是{name}</h1>
      <button onClick={change}>真实情况</button>
    </div>
  )
}

export default App;


effect hooks

相当于生命周期函数


import React from 'react';
import ReactDOM from 'react-dom';

function App() {
const [num,add]=React.useState(0)

React.useEffect(() => {
  let timer=setInterval(() => {
    add(num=>num+1);
  },1000)
  return  ()=>{
    clearInterval(timer)//消失前执行
  }
},[])//第二个参数监听改变的数据,开始就会加载一次

function remove() {
  ReactDOM.unmountComponentAtNode(document.getElementById("root"))
}
  return (
    <div>
      <h1>自加一:{num}</h1>
      <button onClick={remove}>卸载</button>
    </div>
  )
}

export default App;

ref hooks

函数式组件获取ref

import React from 'react';

function App() {
 const myInput=React.useRef()
 function getVal(){
   console.log(myInput.current.value);
 }

  return (
    <div>
      <input type="text" ref={myInput}/>
      <button onClick={getVal}>查看内容</button>
    </div>
  )
}

export default App;

Fragment

去掉最外面的根元素,防止嵌套太多

import React,{Fragment} from 'react';

function App() {
  return (
    <Fragment key={1}>
          {/*可以添加唯一标识key*/}
      <h1>潜龙勿用</h1>
    </Fragment>
  )
}

export default App;

Context

祖孙之前传值

import React, { Component } from 'react';


const MyContext=React.createContext()//
const {Provider,Consumer} = MyContext;//
class App extends Component {
  state={name:'回忆',age:18}
  render() {
    const {name, age} = this.state;
    return (
      <div>
        不忘爸爸
        <Provider value={{name,age}}>
            {/*包裹传值*/}
            <Children />
        </Provider>
      </div>
    );
  }
}

class Children extends Component {
  render() {
    return (
      <div>
        儿子
        <GChild />
      </div>
    );
  }
}

//类组件
// class GChild extends Component {
//   static contextType = MyContext
//   //接上context
//   render() {
//     const {name, age} = this.context;
//      {/*获取值*/}
//     return (
//       <div>
//         孙子:{name}今年{age}了
//       </div>
//     );
//   }
// }


//函数组件
function GChild() {
    return (
      <div>
          孙子:
          <Consumer>
              {/*包裹*/}
            {
              value=>{
                 //值就在value中 
                return value.name+'今年'+value.age+'了'
              }
            }
          </Consumer>
      </div>
    )
}

export default App;

组件优化

pureComponent

当父组件更新时,子组件也会更新,当setState执行,数据不改变时,也会更新,这样会浪费性能

可以设置shouldComponent,判断前后数据是否相同

也可以使用pureComponent

import React, { PureComponent } from 'react';

class App extends PureComponent {
  state={Fname:'不忘'}
  change=()=>{
    this.setState({})
  }
  render() {
    const {Fname} = this.state;
    console.log('father--render');
    return (
      <div>
        {Fname}爸爸
        <Children />
        <button onClick={this.change}>改变</button>
      </div>
    );
  }
}

class Children extends PureComponent {
  render() {
    console.log('children--render');
    return (
      <div>
        儿子
      </div>
    );
  }
}


export default App;

注意内部是浅比较,当数据变化,但用的是同一个对象或者数组时,也不会更新数据

render Props

组件的另外一种套子组件的方式,类似于vue的插槽

import React, { Component } from 'react';

class App extends Component {
  render() {
    return (
      <div>
        不忘爸爸
        <Children>
           <GChild />
        </Children>
      </div>
    );
  }
}

class Children extends Component {
  render() {
    return (
      <div>
        儿子
        {this.props.children/*这里就可以显示Children包裹的内容*/}
      </div>
    );
  }
}

class GChild extends Component {
  render() {
    return (
      <div>
        孙子
      </div>
    );
  }
}


export default App;

但这样不能传值,所有有了升级版renderProps

import React, { Component } from 'react';

class App extends Component {
  render() {
    return (
      <div>
        不忘爸爸
        <Children render={(name)=>(<GChild name={name}/>)}/>
      </div>
    );
  }
}

class Children extends Component {
  state ={name: '回忆'};
  render() {
    return (
      <div>
        儿子
        {this.props.render(this.state.name)}
      </div>
    );
  }
}

class GChild extends Component {
  render() {
    return (
      <div>
        孙子:{this.props.name}
      </div>
    );
  }
}


export default App;

错误边界

如果字组件错误,可以不影响页面的正常运行,将错误限制在组件内部,将bug提交查看

import React, { Component } from 'react';

class App extends Component {
  state={error:''}
  static getDerivedStateFromError(err) {
    return {error:true};
  }

  componentDidCatch(err,info) {
    console.log(err,info);
    //统计错误次数发送给服务器,方便bug解决
  }
  render() {
    return (
      <div>
        不忘爸爸
        {!this.state.error ? '发生错误': <Children/> }
      </div>
    );
  }
}

class Children extends Component {
  state ={man:[
    {id:1,name:'不忘'},
    {id:2,name:'回忆'}
  ]};
  render() {
    return (
      <div>
        儿子
        {this.state.man}
      </div>
    );
  }
}



export default App;

组件传值

1.props

2.消息订阅-发布(pubs-sub等等)

下载包

yarn add pubus-js --sava
import React, { Component } from 'react';
import Pubsub from 'pubsub-js';//引入组件

class App extends Component{
  render() {
    return (
      <div>
        <h1>hello</h1>
        <div id="children">
          <Children />
          <Childrene />
        </div>
      </div>
    );
  };
};

class Children extends Component{
  state = {msg:'天地玄黄,宇宙洪荒。日月盈昃,辰宿列张。'}
  
  componentDidMount(){
    Pubsub.subscribe('getMsg',(_,data)=>{//订阅消息
      this.setState({msg:data});
    })
  }
  render() {
    return (
      <div>
        children:
        <h2>{this.state.msg}</h2>
      </div>
    );
  };
};


class Childrene extends Component{
  state={msg:'道可道,非常道,名可名,非常名'}
  give=()=>{
    Pubsub.publish('getMsg',this.state.msg)//发布消息
  }
  render() {
    return (
      <div>
        childrene:{this.state.msg}
        <button onClick={this.give} >传递数据</button>
      </div>
    );
  };
};

export default App;

3,集中管理(redux)

4。context(生产者-消费者模式)