React调用回调函数,正确设置this指向的三种办法

  1. 通过bind
this.increment = this.increment.bind(this);
  1. 通过箭头函数
<button onClick={this.multi}>点我*10</button> multi = () => {    this.setState({        count: this.state.count * 10    })}
  1. 箭头函数包裹
<button onClick={() => {this.muti2()}}>点我*10</button>  

绑定事件传递参数

通过箭头函数传递事件参数。
<li onClick={(e) => {this.movie(item,index,e)}}>{item}</li>

React实战视频解说:进入学习

条件渲染

  1. 通过if进行条件判断
const {isLogin} = this.state;let welcome = null;if (isLogin) {    welcome = <h2>欢送回来</h2>} else {    welcome = <h2>请先登录!</h2>}
  1. 应用三目运算符
{isLogin ? <h2>欢送回来</h2> : <h2>请先登录!</h2> }
  1. 应用逻辑与
上面这种写法能够省略null。
{isLogin && <h2>你哈欧亚</h2> }

列表渲染

  1. 应用map高阶函数
{    this.state.movies.map((item,index) => {        return (            <li onClick={(e) => {this.movie(item,index,e)}}>                {item}            </li>        )    })}
  1. 应用filter进行过滤
<ul>    {        this.state.scores.filter(item => {            return item >= 60        })    }</ul> 
  1. 应用slice进行截取
区间是左闭右开。
{    this.state.scores.slice(0,3).map(item => {        return <li>{item}</li>    })}

脚手架的根本应用

应用脚手架创立我的项目
  • 项目名称不能蕴含大写字母。
create-react-app demo

组件通信

1. 父组件向子组件传递数据通过props

  • 父组件
export default class App extends Component {  render() {    return (      <div>          <Child  name ='张三' age="18" />      </div>    )  }}
  • 子组件
class Child extends Component {    constructor(props) {        super()        this.props = props;    }    render() {        const {name,age} = this.props;        return (            <div>子组件获取到的name是:{name},age是:{age}</div>        )    }}

2. 子组件向父组件传递数据通过回调函数

import React, { Component } from 'react';class Btn extends Component {    render() {        const {increment} = this.props;        return (            <button onClick={increment}>+1</button>        )    }}class App extends Component {    constructor() {        super();        this.state = {            count: 0        }    }    render() {        const {count} = this.state;        return (            <div>                <h1>以后求和为:{count}</h1>                <Btn increment = {e => this.increment()} />            </div>        );    }    increment() {        console.log(666);        this.setState({            count: this.state.count + 1        })    }}export default App;

3. 跨组件层级通信(Context)(类组件)

import React, { Component } from 'react'const UserContext = React.createContext({    name: '张三',    age: 20})class Sub extends Component {    render() {        return (            <div>                <h1>name是:{this.context.name }</h1>                <h1>age是:{this.context.age}</h1>            </div>        )    }}Sub.contextType = UserContextfunction Profile() {    return (        <div>            <Sub />            <ul>                <li>设置1</li>                <li>设置2</li>                <li>设置3</li>                <li>设置4</li>            </ul>        </div>    )}export default class App extends Component {    constructor(){        super();        this.state = {            name: '李四',            age: 18        }    }    render() {        return (            <div>                <UserContext.Provider value = {this.state}>                    <Profile />                </UserContext.Provider>            </div>        )    }}
上面是函数式组件的写法
function Sub(props) {    return (        <UserContext.Consumer>            {                value => {                    return (                        <div>                            <h1>name是: {value.name}</h1>                            <h1>age是: {value.age}</h1>                        </div>                    )                }            }        </UserContext.Consumer>    )}

4. 任意组件通信(事件总线event bus)

  1. 装置events库
npm install events
  1. 创立eventBus对象
const eventBus = new EventEmitter()
  1. 通过emit发送音讯
<button onClick={e => eventBus.emit('sayHello','Hello Home')}>点击向Home组件发送音讯</button>
  1. 通过addListener来监听音讯
eventBus.addListener('sayHello',(args) => {    this.setState({        message: args    })})
  • 在线CodeSandBox

参数验证

应用PropTypes进行参数验证。
import React from 'react'import PropTypes from 'prop-types'export default function App() {    const names = [1,2,3]    return (        <div>            <Cpn name="张三" age={20} names={names} />        </div>    )}function Cpn(props) {    const { name, age,names } = props;    return (        <div>            <h1>{name} + {age} + </h1>            {                names.map(item => item)            }        </div>    )}Cpn.propTypes = {    names: PropTypes.array,    age: PropTypes.number.isRequired}

React实现slot

通过props进行传递jsx。
  • 父组件
export default class App extends Component {  render() {    return (      <div>          <NavBar               leftSlot={<button>111</button>}              centerSlot={<a href="/#">222</a>}              rightSlot={<span>666</span>}          />         </div>    )  }}
  • 子组件
export default class NavBar extends Component {    render() {        const {leftSlot,centerSlot,rightSlot} = this.props;        return (            <div className='nav-bar'>                <div className="left">                    {leftSlot}                </div>                <div className="center">                    {centerSlot}                </div>                <div className="right">                    {rightSlot}                </div>            </div>        )    }}

性能优化

  1. 函数组件:应用memo
  2. 类组件:应用pureComponent

应用ref操作DOM

在React的开发模式中,通常状况下不须要间接操作DOM,然而某些非凡状况,的确须要间接对DOM进行操作,此时就须要用到Ref。

留神:上面的几种办法都是在类组件中的

  1. 字符串模式的ref
  <div>    <div ref='titleRef'>Hello,React</div>    <button onClick={e => console.log(this.refs.titleRef.innerHTML = 'Hello Ref')}>点击获取题目的DOM元素</button>  </div>
  1. 通过createRef
class App extends Component {  constructor(props) {    super(props);    this.titleRef = createRef();  }  render() {    return (      <div>        <div ref={this.titleRef}>Hello,React</div>        <button onClick={e => console.log(this.titleRef.current.innerHTML = '张三')}>点击获取题目的DOM元素</button>      </div>    );  }}
  1. 回调函数模式的Ref
class App extends Component {  constructor(props) {    super(props);    this.titleRef = null;  }  render() {    return (      <div>        <div ref={arg => this.titleRef = arg}>Hello,React</div>        <button onClick={e => console.log(this.titleRef.innerHTML = '张三')}>点击获取题目的DOM元素</button>      </div>    );  }}
在函数组件中应用ref,能够通过useRef钩子函数
function App() {  const titleRef = useRef();  return (    <div>      <div ref={titleRef}>Hello,React</div>      <button onClick={e => titleRef.current.innerHTML = '张三'}>点击获取题目的DOM元素</button>    </div>  );}

受控组件和非受控组件

受控组件

将可变状态保留在组件的state属性中,并且只能通过应用setState来更新,这种组件叫做受控组件。

上面是一个受控组件的例子:

function App() {  const [msg,setMsg] = useState('');  useEffect(() => {   console.log(msg);   })  return (    <div>      <form onSubmit={e => handleSubmit(e)}>        <label htmlFor="username">          用户:<input                   type="text"                   id="username"                   onChange={e => setMsg(e.target.value)}                   value={msg}                />        </label>        <input type="submit" value="提交" />      </form>    </div>  );}

非受控组件

如果要应用非受控组件中的数据,须要应用ref来从DOM节点中获取表单数据。

高阶组件

高阶组件是一个接管参数为组件,返回值为新组件的函数。留神:高阶组件是一个函数。

上面是一个高阶组件的实例:

class App extends PureComponent {  render() {    return (      <div>        App        {this.props.name}      </div>    )  }}function enhanceComponent(WrappedComponent) {  return class newComponent extends PureComponent {    render() {      return <WrappedComponent {...this.props} />    }  }}const EnhanceComponent = enhanceComponent(App)export default EnhanceComponent

高阶组件的利用一:加强props

function Home(props) {  return (    <h1>昵称:{props.nickname}  等级: {props.level} 区域:{props.region}</h1>  )}function enhanceProps(Cpn) {  return props => {    return <Cpn {...props} region="中国" />  }}const EnhanceHome = enhanceProps(Home)class App extends PureComponent {  render() {    return (      <div>        <EnhanceHome nickname="张三" level="99" />      </div>    )  }}export default App

高阶组件的其余利用

高阶组件还能够用于登录鉴权、生命周期劫持(这里的生命周期劫持,咱们能够了解为计算某个组件的渲染工夫)、通过forwardRef高阶函数给函数式组件传递Ref,具体不再赘述。

portals的应用

portals存在的意义在于,有时候咱们想要一个组件独立于父组件进行渲染,例如这样的一个场景:父组件的显示区域比拟小,然而咱们想要一个组件显示在屏幕的两头,此时就能够应用portals。

上面这个例子是将Modal组件渲染到屏幕的两头。

function Modal(props) {  return (    ReactDOM.createPortal(props.children,document.querySelector('#modal'))  )}function Home(props) {  return (    <div>      <h1>Home</h1>      <Modal>        <h2>Title</h2>      </Modal>    </div>  )}export default class App extends PureComponent {  render() {    return (      <div>        <Home />      </div>    )  }}

fragment

所谓的fragment就是应用空标签来代替div标签,防止出现不必要的标签。
<>  <h1>以后求和为:{count}</h1>  <button onClick={e => setCount(count + 1)}>点我+1</button></>
上面这种写法,能够增加属性,下面的写法令不行。
<Fragment>  <h1>以后求和为:{count}</h1>  <button onClick={e => setCount(count + 1)}>点我+1</button></Fragment>

React中的CSS

内联款式

长处:
  1. 不会有抵触。
  2. 能够动静获取以后state中的动静。
export default function App() {  const pStyle = {    color: 'pink'  }  return (    <div>      <h2 style={{color: 'red'}}>这是题目</h2>      <p style={pStyle}>这是一段文字</p>    </div>  )}
毛病:
  1. 写法上须要应用驼峰标识。
  2. 某些款式没有提醒。
  3. 大量的款式,代码凌乱。
  4. 某些款式无奈编写,例如伪类、伪元素。

组件文件夹下独自引入css

这种形式容易呈现款式笼罩的问题。

CSS modules

CSS modules能够无效的解决款式笼罩的问题。
  1. 在组件文件夹下编写CSS文件,留神后缀是.module.css
  2. 组件中引入款式
import style from './style.module.css'
  1. 通过类名的形式应用css
export default function App() {  return (    <div>      <h1 className={style.title}>这是APP</h1>    </div>  )}
从这种形式咱们能够看出显著要好于独自写CSS。然而这种计划也有其毛病,就是援用的类名中不能蕴含短横线,这样无奈辨认,不不便动静批改某些款式。

CSS IN JS

CSS-in-JS是一种模式,其中CSS由JS生成而不是在内部文件中定义,此性能不是React的一部分,而是由第三方库提供。

目前比拟风行的CSS-in-JS库有:

  • styled-components(应用最多的)
  • emotion
  • glamorous
在应用CSS-in-JS之前,咱们须要把握标签模板字符串的用法,上面是一个经典的例子:
function test(...arg) {  console.log(arg);  // [['123 is ', ''], '张三']}const name = '张三'test`123 is ${name}`
上面介绍下,如何应用styled-components。
  1. 装置
npm install styled-components
  1. 引入styled-components
import styled from 'styled-components'
  1. 创立带款式的组件(留神:款式没有加引号)
const Wrapper = styled.h1`  color: red;`
  1. 应用带款式的组件替换原生组件
<Wrapper>这是APP组件</Wrapper>
styled-components也是反对less等写法的,例如上面的例子:
const Wrapper = styled.div`  color: red;  .banner {    background-color: blue;  }  // 留神:这里也能够应用props的写法来获取动静属性`
给css-in-js传递动静属性。
import React,{useState} from 'react'import styled from 'styled-components'const Wrapper = styled.div.attrs({  bColor: "red"})`  background-color: lightblue;  border: 2px solid;  border-color: ${props => props.bColor};  color: ${props => props.color};`export default function App() {  const [color] = useState('yellow')  return (    <div>      <Wrapper color={color}>        这是APP组件        <h2 className="banner">这是H2</h2>      </Wrapper>    </div>  )}

应用classnames库给React动静增加className

  1. 装置库
npm install classnames
  1. 引入库
import classNames from 'classnames';
  1. 以对象的模式动静增加className
function App() {  const [isActive] = useState(true);  return (    <div className="App">      <h1 className={classNames({"active": isActive})}>这是APP</h1>    </div>  );}
如果想要赋值常量,间接传入一般的字符串即可,以逗号宰割。

Antd的根本应用

脚手架场景下

  1. 装置antd
npm install antd
  1. 引入antd和对应的css款式
import { Button, Space } from 'antd';import { PoweroffOutlined } from '@ant-design/icons';import './App.css'
  1. 依据官网组件的实例代码进行批改。

通过craco对antd主题进行配置

  1. 装置@craco
npm install @craco
  1. 自定义主题实际上须要装置上面三个包
"@craco/craco": "^6.4.3","babel-plugin-import": "^1.13.5","craco-less": "^2.0.0","less-loader": "^10.2.0"
  1. craco.config.js
const CracoLessPlugin = require('craco-less');module.exports = {  babel: {      plugins: [         [             "import",              {                 "libraryName": "antd",                 "libraryDirectory": "es",                  "style": true //设置为true即是less              }          ]      ]  },  plugins: [      {          plugin: CracoLessPlugin,          options: {              lessLoaderOptions: {                  lessOptions: {                      modifyVars: { '@primary-color': '#7d2b21' },                      javascriptEnabled: true,                  },              },          },      },  ],};
强烈建议应用yarn,不要应用antd。

给文件夹门路起别名

首先,之所以要给文件夹起别名,就是因为有时候文件的嵌套层级比拟深,不好找到文件,然而通过给根文件夹起别名则能够很快的找到它们。

在配置文件中进行如下配置:

const path = require('path')// 将参数的门路和以后的门路进行一个拼接const resolve = dir => path.resolve(__dirname, dir);module.exports = {    webpack: {        alias: {            "@": resolve("src"),            "components": resolve("src/components")      }    }};
引入文件门路的时候则能够这样引入:”
import Test from 'components/Test'

评论组件案例

  • codesandbox在线代码

axios的应用和封装

  1. 装置axios
yarn add axios
  1. 引入axios
import axios from 'axios'
  1. 发送get申请
axios({  url: "https://httpbin.org/get",  params: {    name: '张三',    age: 20  }}).then(res => {  console.log(res);}).catch(err => {  console.log(err);})
还能够通过上面的形式:
axios.get("https://httpbin.org/get", {  params: {    name: '张三',    age: 20  }}).then(res => console.log(res))
  1. 发送post申请
axios({  url: "https://httpbin.org/post",  data: {    name: 'edge',    age: 0  },  method: "post"}).then(res => {  console.log(res);}).catch(err => {  console.error(err)})
也能够通过上面这种形式:
axios.post("https://httpbin.org/post", {  data: {    name: 'edge',    age: 0  }}).then(console.log)
  1. axios联合async和await
  useEffect(() => {    async function fetchData() {      const result = await axios.post("https://httpbin.org/post", {        data: {          name: 'edge',          age: 0        }      })      console.log('111',result);    }    fetchData()  }, [])
  1. axios.all的应用
const request1 = axios({  url: "https://httpbin.org/get",  params: {name: "test",age: 20}})const request2 = axios({  url: "https://httpbin.org/post",  data: {name: 'kobe',age: 66},  method: 'post'})axios.all([request1,request2]).then(([res1,res2]) => {  console.log('axios.all:',res1,res2);}).catch(err => {  console.log(err);})
  1. 配置多个申请的独特信息
在引入axios的中央进行如下配置:
axios.defaults.baseURL = "https://httpbin.org";axios.defaults.timeout = 5000;axios.defaults.headers.common["token"] = "dfasdfkajndsfkjndsf";axios.defaults.headers.post["Content-type"] = "application/text"
配置后的申请则能够这样写:
const request1 = axios({  url: "/get",  params: {name: "test",age: 20}})const request2 = axios({  url: "/post",  data: {name: 'kobe',age: 66},  method: 'post'})
  1. 创立axios实例来实现个性化申请不同的服务器
下面咱们提到了创立公共申请的配置信息,然而有时候咱们想要申请的URL可能是不同的地址,此时就须要个性化的配置了。
const instance2 = axios.create({  baseURL: "http://baidu.xyz",  timeout: 1000})instance2.get('/get',{  params: {data: "test"}}).then(res => console.log(res)).catch(err => console.log(err))
  1. axios拦截器
axios.interceptors.request.use(config => {  // 1. 能够在这个地位设置显示loading组件  // 2. 给申请增加token  // 3. 对params进行序列化的操作  console.log('拦挡胜利');  config.headers.token = JSON.stringify({ name: 'ty' });  return config}, err => {})// // 响应拦截器axios.interceptors.response.use(res => {  // res.data = 666;  console.log('响应拦截器拦挡胜利');  return res}, err => {})axios.get('https://httpbin.org/get', {  params: { name: 'justin' }}).then(console.log).catch(console.log)axios.post('https://httpbin.org/post', {  data: { name: 'justin6366666666' }}).then(res => console.log('响应:',res)).catch(console.log)

二次封装axios

之所以要对axios进行二次封装,次要就是一旦申请不能应用了,只须要批改一个文件即可,同时封装能够缩小很多反复代码的编写。
  1. 创立一个service文件夹
  2. service文件夹下创立一个request.js
  3. service文件夹下创立一个config.js(用于书写axios的公共配置信息)
config.js中能够写上面的配置信息:
const devBaseURL = "https://httpbin.org";const proBaseURL = "https://production.org";export const BASE_URL = process.env.NODE_ENV === 'development' ? devBaseURL : proBaseURL;export const TIMEOUT = 5000;
request.js中能够写上面的申请办法:
import axios from "axios";import {BASE_URL,TIMEOUT} from './config'const instance = axios.create({  baseURL: BASE_URL,  timeout: TIMEOUT})export default instance

React Hooks

为什么须要Hooks?

Hook是React16.8中新增的个性,它能够让咱们在不编写class的状况下应用state以及其余的React个性。

在Hook呈现之前,函数式组件绝对于class组件有如下劣势:

  • class组件能够定义本人的状态,函数式组件不能够。
  • class组件有本人的生命周期,函数式组件则会每次从新渲染都从新发送一次网络申请。
  • 函数式组件在从新渲染时整个函数都会被执行。

class组件存在的问题

  1. 随着业务的减少,逻辑的简单,class组件可能会变得越来越简单,比方componetDidMount中可能蕴含大量的逻辑代码,这样的class实际上难以拆分,逻辑混在一起,代码的复杂度比拟高。
  2. class组件中的this指向比较复杂,难以了解。
  3. 组件复用状态难。例如咱们应用Provider、Consumer来共享状态,然而屡次应用Consumer时,咱们的代码就会存在很多嵌套。

为什么叫做Hook?

Hook间接翻译可能是钩子的意思,意味着这类函数能够帮忙咱们钩入React的state以及生命周期等个性。

应用Hooks的两个规定

  1. 只能在函数最外层调用Hook,不要在循环、条件判断、或者子函数中调用。
  2. 只能在React的函数式组件中调用Hook,不能在JS函数中调用。

useState的外围用法

useState能够接管一个函数,也能够接管一个值,如果是函数,其能够拿到前一个状态,然而返回的要是最新的状态,如果是值的话,就应该是返回的最新状态。
<button onClick={e => setCount(precount => precount + 1)}>点击+1</button><button onClick={e => setFriends([...friends,'匿名'])}>点击增加敌人</button>

useEffect的外围用法

useEffect次要是用来模仿生命周期。

  • useEffect在一个函数组件中能够定义多个,并依照程序执行。
  useEffect(() => {    console.log('批改DOM');  })  useEffect(() => {    console.log('订阅事件');  },[])
  • 检测某个状态发生变化的时候才执行回调函数。
  useEffect(() => {    console.log('订阅事件');  },[count])

useContext的外围用法

// 1. 创立一个xxxContextconst countContext = createContext();// 2. 通过xxxContext.Provider 包裹传递value给指标组件function App() {    return (        <countContext.Provider value={666}>            <Foo />        </countContext.Provider>    )}// 3. 指标组件通过useContext(xxxContext)获取value传递的值function Foo() {    const count = useContext(countContext)    return (        <div>            {count}        </div>    )}

useReducer的外围用法

useReducer是useState的一种代替计划。
const reducer = (state,action) => {  switch (action.type) {    case "increment":      return {...state,count: state.count + 1}    default:      return state;  }}export default function Home() {  const [count,dispatch] = useReducer(reducer,{count: 0});  return (    <div>        <h1>以后求和为:{count.count}</h1>        <button onClick={e => dispatch({type: 'increment'})}>点我+1</button>    </div>  )}

useCallback的外围用法

useCallback会返回一个函数的memorized值,在依赖不变的状况下,屡次定义的时候,返回的值是雷同的。

useCallback想要解决的问题是这样的,如果一个函数组件中有一个函数,只有状态产生扭转,这个函数都会被从新定义,非常节约性能,并且可能带来不好的影响。

useCallback联合memo能够进行性能优化,确保传入的是雷同的函数实例。useCallback如果依赖项是一个空数组,则只会执行一次,返回的都是雷同的函数实例,如果有依赖项的话,则是依赖项发生变化才返回新的实例。

常见的应用场景是:将一个函数传递给组件进行回调时,能够进行性能优化。

export default function CallbackDemo() {  console.log('CallbackDemo被从新渲染');  const [count, setCount] = useState(0);  const [show,setShow] = useState(true);  const increment1 = useCallback(() => {    console.log('increment1函数执行了~');    setCount(count + 1);  })  const increment2 = useCallback(() => {    console.log('increment2函数执行了~');    setCount(count + 1);  }, [count])  return (    <div>      <h1>以后求和为:{count}</h1>      <MyButton title="btn1" increment={increment1} />      <MyButton title="btn2" increment={increment2} />      <button onClick={e => setShow(!show)}>点击切换show</button>    </div>  )}

useMemo的外围用法

useMemo的外围也是为了性能优化。
  • useMemo返回的也是一个缓存的值。
  • 依赖不变的状况下,屡次定义的时候,返回的值是雷同的。
上面的这个例子能够很好的阐明useMemo的外围用法,能够无效的防止calc函数不必要的从新计算。
const calc = (num) => {  console.log('从新计算');  let temp = 0;  for (let i = 1; i <= num; i++) {    temp += i;  }  return temp;}export default function MemoHookDemo() {  const [count, setCount] = useState(10);  const [show,setShow] = useState(true);  // 防止calc函数呈现不必要的从新计算  const total = useMemo(() => {    return calc(count);  },[count])  return (    <div>      <h1>以后求和为:{total}</h1>      <button onClick={e => setCount(count + 1)}>点击+1</button>      <button onClick={e => setShow(!show)}>点击切换</button>    </div>  )}
useMemo还能够防止子组件不必要的从新渲染。(联合了memo)
const MyButton = memo(props => {  console.log('子组件从新渲染');  return (    <h2>子组件收到的props:{props.info.name}</h2>  )})export default function MemoHookDemo02() {  console.log('父组件从新渲染');  const [show,setShow] = useState(false);  const info = useMemo(() => {    return {name: "漫威"}  },[])  return (    <div>      <MyButton info={info} />      <button onClick={e => setShow(!show)}>点击切换</button>    </div>  )}

useRef的外围用法

  1. 应用ref援用DOM。
export default function RefHook() {  const titleRef = useRef();  const changeDOM = () => {    titleRef.current.innerHTML = "援用DOM"  }  return (    <div>      <h2 ref={titleRef}>这是useRef的外围用法</h2>      <button onClick={e => changeDOM()}>点击切换</button>    </div>  )}
  1. 函数式组件是不能间接给ref的。
函数组件能够通过React.forwardRef进行包裹来应用ref。
const Test = React.forwardRef((props,ref) => {  return (    <div>      <h1>这是Test组件</h1>    </div>  )})
  1. 应用useRef跨足剑周期保留数据
export default function RefHook() {  const [count,setCount] = useState(0);  const countRef = useRef(count);  return (    <div>      <h2>useRef中保留的值:{countRef.current}</h2>      <h2>这是count的值:{count}</h2>      <button onClick={e => setCount(count + 1)}>点击+1</button>    </div>  )}

useImperativeHandle的外围用法

之所以要有useImperativeHandle这个钩子函数,是为了避免父组件通过ref获取到子组件的所有权限,通过useImperativeHandle能够让子组件指定对外裸露的性能。
const Son = forwardRef((props,ref) => {  const inputRef = useRef();  useImperativeHandle(ref,() => ({    focus: () => {      inputRef.current.focus();    }  }))  return (    <input type="text" ref={inputRef} />  )})export default function RefHook() {  const sonRef = useRef()  return (    <div>      <Son ref={sonRef} />      <button onClick={e => sonRef.current.focus()}>点击聚焦</button>    </div>  )}

useLayoutEffect的外围用法

useLayoutEffect和useEffect的区别次要有以下两点:
  • useEffect是在DOM更新实现之后执行,不会阻塞DOM的更新。
  • useLayoutEffect会在更新DOM之前执行,会阻塞DOM的更新。
如果心愿在某些操作产生之后再去更新DOM,那么这个操作应该放在useLayoutEffect中执行。次要是解决闪动问题。
export default function LayoutDemo() {  const [count,setCount] = useState(10);  // useEffect会在渲染之后执行  // useEffect(() => {  //   if (count === 0) {  //     setCount(Math.random());  //   }  // },[count])  // useLayoutEffect会在渲染之前执行  useLayoutEffect(() => {    if (count === 0) {      setCount(Math.random());    }  },[count])  return (    <div>      <h1>以后随机数为:{count}</h1>      <button onClick={e => setCount(0)}>点击设置为随机数</button>    </div>  )}

自定义Hook的外围用法(次要还是用于逻辑复用)

自定义Hook的实质是一种函数代码逻辑的抽取。自定义组件必须以use结尾,否则会报错。

上面的这个自定义Hook就是对组件的挂载和卸载中反复的逻辑进行复用。

export default function CustomHook() {  useInfo('CustomHook');  return (    <div>      <h1>这是测试自定义Hook</h1>    </div>  )}function useInfo(name) {  useEffect(() => {    console.log(`${name}组件被挂载了~`);    return () => {      console.log(`${name}组件被卸载了~`);    };  }, []);}
自定义Hook和一般的函数封装的区别在于,自定义Hook能够应用默认的Hooks,相似于useState等,然而一般的函数不能应用,这也就是为什么自定义Hook在命名时须要以use结尾。

react-router的外围用法

装置react-router-dom
yarn add react-router-dom

react-router中最外围的API

BrowserRouter和HashRouter

  • Router中蕴含了对门路扭转的监听,并且会将相应的门路传递给子组件。
  • BrowserRouter应用History模式。
  • HashRouter应用Hash模式。

Link和NavLink

  • 个别门路的跳转应用Link组件,其最终会被渲染成a元素。
  • NavLink是在Link根底上减少一些款式属性。
  • to属性,指定跳转到的门路。

Route

  • Route用于门路的匹配
  • path属性:用于设置匹配到的门路。
  • component属性:设置匹配到的门路后,渲染的组件。
  • exact:精准匹配,只有精准匹配到完全一致的门路,才会渲染对应的组件。

根本应用

上面应用的是一些新个性:
export default function RouteTest() {  return (    <div>      <BrowserRouter>        <Link to="/" >首页</Link>        <Link to="/about" >对于</Link>        <Routes>          {/* <Route path="/" component={Home} />          <Route path="/about" component={About} /> */}          {/* 上面是React18的新语法 */}          <Route path="/" element={<Home />} />          <Route path="/about" element={<About />} />        </Routes>      </BrowserRouter>    </div>  )}

留神:Link都会显示成a标签,然而并不是所有的Route都会显示,Route所在的区域就是命中路由的组件要显示的区域。咱们能够把Route了解为占位符。

react-router的不同版本的特点都是不一样的,因而,有些特定性能的用法肯定要依据版本去官网查用法,例如上面的这个给选中的link扭转色彩,就是通过这个版本对应的官网查到的。

  • react-router-V6
export default function RouteTest() {  let activeStyle = {    color: "red",  };  return (    <div>      <BrowserRouter>        <Routes>          {/* <Route path="/" component={Home} />          <Route path="/about" component={About} /> */}          {/* 上面是React18的新语法 */}          <Route exact path="/" element={<Home />} />          <Route exact path="/about" element={<About />} />        </Routes>        <NavLink to="/"           style={({ isActive }) =>          isActive ? activeStyle : undefined        }>          首页        </NavLink>        <NavLink to="/about"          style={({ isActive }) =>            isActive ? activeStyle : undefined          }>          对于        </NavLink>      </BrowserRouter>    </div>  )}
须要留神的是在react-router(V6)版本中Switch曾经被Routes取代了。

路由重定向

重定向和Link的区别在于,Link是须要用户点击的,重定向能够是JS执行的。

在V6版本的react-router-dom中重定向Redirect曾经被Navicat这个API取代了、

import {Navigate} from 'react-router-dom'const User = () => {  const [isLogin] = useState(false);  return isLogin ? (    <div>      <h1>这是User组件</h1>    </div>  ) : <Navigate to="/login"/>;}

动静路由

须要留神的是,设置动静路由的时候最好在某个门路下应用,而不是间接就是一个动静路由,那样容易呈现拦挡到意外路由的状况。
<Route  path="/user/:id" element={<User />} />
  • 应用useParams获取动静路由的值。
import {Navigate,useParams} from 'react-router-dom'const User = () => {  const [isLogin] = useState(true);  const params = useParams();  console.log(params);  return isLogin ? (    <div>      <h1>这是User组件</h1>    </div>  ) : <Navigate to="/login"/>;}
  • 应用useSearchParams获取查问字符串(通过原型对象上的get办法来获取值)
import {Navigate,useSearchParams} from 'react-router-dom'const User = () => {  const [isLogin] = useState(true);  const [searchParams,setSearchParams] = useSearchParams();  console.log(searchParams);  console.log(searchParams.get('name'));  return isLogin ? (    <div>      <h1>这是User组件</h1>    </div>  ) : <Navigate to="/login"/>;}
  • 应用遍历的形式获取到所有的查问字符串
import {Navigate,useSearchParams} from 'react-router-dom'const User = () => {  const [isLogin] = useState(true);  const [searchParams,setSearchParams] = useSearchParams();  searchParams.forEach((item,key) => {    console.log(item,key);  })  console.log(searchParams.get('name'));  return isLogin ? (    <div>      <h1>这是User组件</h1>    </div>  ) : <Navigate to="/login"/>;}

应用react-router-config简化路由的编写

具体能够通过查看官网应用。
  • react-router-config

嵌套路由

嵌套路由咱们能够了解为路由中的路由。(须要应用Outlet进行占位,具体看上面的链接中的文章。)
  <BrowserRouter>    <Routes>      <Route exact path="/about" element={<About />}>        <Route path="culture" element={<AboutCulture />} />        <Route path="contact" element={<AboutContact />} />      </Route>  </BrowserRouter>
  • react-router v6 应用(这篇文章讲的特地好)

手动路由跳转

在react-router-dom 6版本中history这个API被useNavigate取代了。
const About = () => {  const navigate = useNavigate()  return (    <div>      <NavLink to="/about/culture">企业文化</NavLink>      <NavLink to="/about/contact">分割咱们</NavLink>      <button onClick={e => navigate('/about/join')}>点击退出咱们吧~</button>      <Outlet />    </div>  );}