关于react.js:react进阶用法完全指南

38次阅读

共计 20217 个字符,预计需要花费 51 分钟才能阅读完成。

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 = UserContext
function 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. 创立一个 xxxContext
const 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>
  );
}

正文完
 0