使用react搭建组件库(二):react+typescript
<p>1 使用了react官方脚手架:create-react-app</p><p>https://github.com/facebook/create-react-app</p>
<h2><code>npm run eject 可以打开配置文件</code></h2>
<p>自定义配置文件</p>
<p>执行安装: <span class="cnblogs_code">npx create-react-app ts-<span style="color: rgba(0, 0, 255, 1)">with</span>-react --typescript</span> </p>
<p>npx 只有在npm5.2以上版本才有</p>
<p>1、避免安装全局模块:临时命令,使用后删除,再次执行的时候再次下载</p>
<p>2、调用项目内部安装的模块用起来更方便:比如 在package.json文件中安装了一个依赖:mocha,如果想执行有两种方法:</p>
<p> 2.1 在scripts中定义 </p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">{
</span>"script"<span style="color: rgba(0, 0, 0, 1)">:{
</span>"test":"mocha --version"<span style="color: rgba(0, 0, 0, 1)">
}
}</span></pre>
</div>
<p>2.2 在命令行中执行 <span class="cnblogs_code">node_modules/.bin/mocha --version</span> </p>
<p>而使用npx的话 则只需要执行: <span class="cnblogs_code">npx mocha --version</span> </p>
<p>首先新建一个hello组件:components/hello.jsx</p>
<div class="cnblogs_code">
<pre>import React from 'react'<span style="color: rgba(0, 0, 0, 1)">
interface IHelloProps {
message</span>?: string;<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">因为设置了props的默认值,这里用?表示有无都可</span>
<span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">
const Hello = (props:IHelloProps) => {
return <h2>{props.message}</h2>
}
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
const Hello: React.FC</span><IHelloProps> = (props) =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">FC是 react 官方提供的一个 interface 的简写:FunctionComponent,接收一个范型</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">这样Hello包含了很多静态方法</span>
<span style="color: rgba(0, 0, 255, 1)">return</span> <h2>{props.message}</h2>
<span style="color: rgba(0, 0, 0, 1)">}
Hello.defaultProps </span>=<span style="color: rgba(0, 0, 0, 1)"> {
message: </span>"Hello World"<span style="color: rgba(0, 0, 0, 1)">
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> Hello</pre>
</div>
<p> </p>
<p>## 什么是react hook</p>
<p>React 16.8 带来的全新特性,使用函数式组件,即将替代class组件的写法;</p>
<p>解决的问题</p>
<p>1.组件很难复用状态逻辑,一般使用HOC或者render Props</p>
<p>2.复杂组件难以理解,尤其是生命周期函数</p>
<p><img src="https://img2020.cnblogs.com/blog/693756/202004/693756-20200405141414210-2063235129.png"></p>
<p>例如,获取props中的id,需要在 componentDidMount和 componentDidUpdate 中同时定义;监听函数也需要在 componentDidMounted和componentWillUnmount中监听和注销</p>
<p>3.react组件一直是函数,使用Hook完全拥抱函数</p>
<p> </p>
<p>## State Hook</p>
<p> 新建一个likeBotton.tsx 文件【点赞按钮】</p>
<div class="cnblogs_code">
<pre>import React, { useState } from 'react'<span style="color: rgba(0, 0, 0, 1)">
const LikeButton: React.FC </span>= () =><span style="color: rgba(0, 0, 0, 1)"> {
const </span>= useState(0<span style="color: rgba(0, 0, 0, 1)">);
const </span>= useState(<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><>
<button onClick={() => {setLike(like + 1)}}><span style="color: rgba(0, 0, 0, 1)">
{like} 👍
</span></button>
<button onClick={() => {setOn(!on)}}><span style="color: rgba(0, 0, 0, 1)">
{on</span>?'ON':'OFF'<span style="color: rgba(0, 0, 0, 1)">}
</span></button>
</>
<span style="color: rgba(0, 0, 0, 1)">)
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> LikeButton</pre>
</div>
<p>网络请求、DOM操作 和函数渲染页面关系不大的 放在副作用中</p>
<p>Effect Hook</p>
<p>1.无需清除的Effect</p>
<p>2.需要清除的Effect</p>
<p>如果是以前的话,需要在两个生命周期中执行同样的代码</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">componentDidMount(){
document.title </span>= `you clicked ${<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.count} times`;
}
componentDidUpdate(){
document.title </span>= `you clicked ${<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.count} times`;
}</span></pre>
</div>
<p>使用useEffect:</p>
<div class="cnblogs_code">
<pre>import React, { useState,useEffect } from 'react'<span style="color: rgba(0, 0, 0, 1)">
const LikeButton: React.FC </span>= () =><span style="color: rgba(0, 0, 0, 1)"> {
const </span>= useState(0<span style="color: rgba(0, 0, 0, 1)">);
const </span>= useState(<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">);
useEffect(()</span>=>{<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">useEffect会在组件每次渲染的时候,都会被调用</span>
document.title =<span style="color: rgba(0, 0, 0, 1)"> `点击了${like}次`
})
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><>
<button onClick={() => {setLike(like + 1)}}><span style="color: rgba(0, 0, 0, 1)">
{like} 👍
</span></button>
<button onClick={() => {setOn(!on)}}><span style="color: rgba(0, 0, 0, 1)">
{on</span>?'ON':'OFF'<span style="color: rgba(0, 0, 0, 1)">}
</span></button>
</>
<span style="color: rgba(0, 0, 0, 1)">)
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> LikeButton</pre>
</div>
<p>2.需要清除的Effect</p>
<p>功能: 使用useEffect 完成一个鼠标跟踪器</p>
<p>原来在class中的实现方法:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">componentDidMount(){
document.addEventListener(</span>'click',<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.updateMouse);
}
componentWillUnMount(){
document.addEventListener(</span>'click',<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.updateMouse);
}</span></pre>
</div>
<p>使用useEffect,就要忘记以前的生命周期,页面挂载之后执行useEffect</p>
<div class="cnblogs_code">
<pre>import React, { useState, useEffect } from 'react'<span style="color: rgba(0, 0, 0, 1)">
const MouseTracker: React.FC </span>= () =><span style="color: rgba(0, 0, 0, 1)"> {
const [ positions, setPositions ] </span>= useState({x: 0, y: 0<span style="color: rgba(0, 0, 0, 1)">})
useEffect(() </span>=><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>'add effect', positions.x)<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">执行顺序2:add effect 0;//执行顺序6:add effect 102</span>
const updateMouse= (e: MouseEvent) =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>'inner') <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">执行顺序3</span>
<span style="color: rgba(0, 0, 0, 1)"> setPositions({ x: e.clientX, y: e.clientY })
}
document.addEventListener(</span>'click'<span style="color: rgba(0, 0, 0, 1)">, updateMouse)
</span><span style="color: rgba(0, 0, 255, 1)">return</span> () => { <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">如果不加return函数中卸载功能,每次渲染函数组件的时候,都会执行useEffect</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">导致注册很多个 监听事件,</span>
console.log('remove effect', positions.x) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">执行顺序5:remove effect 0</span>
document.removeEventListener('click'<span style="color: rgba(0, 0, 0, 1)">, updateMouse)
}
}, [])
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">执行顺序1:before render 0//点击页面后,执行顺序4:before render 102</span>
console.log('before render'<span style="color: rgba(0, 0, 0, 1)">, positions.x)
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><p>X: {positions.x}, Y : {positions.y}</p>
<span style="color: rgba(0, 0, 0, 1)">)
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> MouseTracker</pre>
</div>
<p> </p>
<p>但是每次更新函数组件,都要执行useEffect ,下面介绍如何控制useEffect函数的执行</p>
<div class="cnblogs_code">
<pre>useEffect(() =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>'document title effect is running'<span style="color: rgba(0, 0, 0, 1)">)
document.title </span>=<span style="color: rgba(0, 0, 0, 1)"> `点击了${like}次`;
document.addEventListener(</span>'click'<span style="color: rgba(0, 0, 0, 1)">, updateMouse)
</span><span style="color: rgba(0, 0, 255, 1)">return</span> ()=><span style="color: rgba(0, 0, 0, 1)">{
document.removeEventListener(</span>'click'<span style="color: rgba(0, 0, 0, 1)">, updateMouse)
}
}, [])
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">useEffect第二个参数,如果是空数组,表示只在挂载元素后执行1次,但是其返回函数,会在组件卸载的时候执行</span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)">如果不写第二个参数,则不做限制,组件中的任何一个data变化的时候,都会执行useEffect函数</span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)">如果第二个参数写上变量后,则只有该变量发生变化的时候,才执行 useEffect</span></pre>
</div>
<p>## 自定义Hook——新建的hooks,<span style="color: rgba(255, 0, 0, 1)"><strong>必须以 use 开头</strong></span></p>
<p>1 将组件逻辑提取到可重用的函数中</p>
<p> 例如两个组件中均用到了公共的逻辑,获取跟随鼠标移动的坐标:</p>
<p>新建hooks/useMousePosition.jsx</p>
<div class="cnblogs_code">
<pre>import React, { useState, useEffect } from 'react'<span style="color: rgba(0, 0, 0, 1)">
const useMousePosition </span>= () =><span style="color: rgba(0, 0, 0, 1)"> {
const [ positions, setPositions ] </span>= useState({x: 0, y: 0<span style="color: rgba(0, 0, 0, 1)">})
useEffect(() </span>=><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>'add effect'<span style="color: rgba(0, 0, 0, 1)">, positions.x)
const updateMouse</span>= (e: MouseEvent) =><span style="color: rgba(0, 0, 0, 1)"> {
setPositions({ x: e.clientX, y: e.clientY })
}
document.addEventListener(</span>'mousemove'<span style="color: rgba(0, 0, 0, 1)">, updateMouse)
</span><span style="color: rgba(0, 0, 255, 1)">return</span> () =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>'remove effect'<span style="color: rgba(0, 0, 0, 1)">, positions.x)
document.removeEventListener(</span>'mousemove'<span style="color: rgba(0, 0, 0, 1)">, updateMouse)
}
}, [])
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> positions
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> useMousePosition</pre>
</div>
<p>在其他组件中引入方式:</p>
<div class="cnblogs_code">
<pre>import useMousePosition from './hooks/useMousePosition'<span style="color: rgba(0, 0, 0, 1)">
const App:React.FC </span>= () =><span style="color: rgba(0, 0, 0, 1)"> {
const positions</span>=<span style="color: rgba(0, 0, 0, 1)"> useMousePosition();
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div>
<p>X:{position.x},Y:{positions.y}</p>
</div>
<span style="color: rgba(0, 0, 0, 1)">)
}</span></pre>
</div>
<p> </p>
<p>## 编辑一个hooks,功能是:加载图片功能,替换之前的HOC方式,简单方便,相当于定义一个函数,然后再外面用到的地方调用,可以传参,可以控制调用的时机:<br><br></p>
<div>
<div>react 中的 HOC 高阶组件,就是一个函数,接受一个组件作为参数,返回一个新的组件;</div>
<div>react可以通过高阶组件来扩展,而vue需要通过mixins来扩展。</div>
<div> </div>
<div> </div>
<div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">interface IShowResult{
message:string,
status:string
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">定义一个组件</span>
const DogShow:React.FC<{data:IShowResult}> = ({data})=><span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><>
<h2>show:{data.status}</h2>
<img src={data.message}/>
</>
<span style="color: rgba(0, 0, 0, 1)"> )
}
const App:React.FC</span>=()=><span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">高阶组件,将一个组件用参数形式传入,然后经过包裹后返回一个新的组件,达到公用包裹组件的功能</span>
const WrappedDogShow = withLoader(DogShow,'https://dog.ceo/api/breeds/image/random'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><WrappedDogShow/>
<span style="color: rgba(0, 0, 0, 1)"> )
}</span></pre>
</div>
<p>高阶组件</p>
<p> </p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> high order component</span>
import React from 'react'<span style="color: rgba(0, 0, 0, 1)">
import axios from </span>'axios'<span style="color: rgba(0, 0, 0, 1)">
interface ILoaderState {
data: any,
isLoading: </span><span style="color: rgba(0, 0, 255, 1)">boolean</span><span style="color: rgba(0, 0, 0, 1)">
}
interface ILoaderProps {
data: any,
}
const withLoader </span>= <P extends ILoaderState>(WrappedComponent: React.ComponentType<P>, url: string) =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> class LoaderComponent extends React.Component<Partial<ILoaderProps>, ILoaderState><span style="color: rgba(0, 0, 0, 1)"> {
constructor(props: any) {
super(props)
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.state =<span style="color: rgba(0, 0, 0, 1)"> {
data: </span><span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">,
isLoading: </span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">
}
}
componentDidMount() {
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({
isLoading: </span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">,
})
axios.get(url).then(result </span>=><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({
data: result.data,
isLoading: </span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">
})
})
}
render() {
const { data, isLoading } </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><><span style="color: rgba(0, 0, 0, 1)">
{ (isLoading </span>|| !data) ? <p>data is loading</p> :
<WrappedComponent {...<span style="color: rgba(0, 0, 255, 1)">this</span>.props as P} data={data} />
<span style="color: rgba(0, 0, 0, 1)"> }
</span></>
<span style="color: rgba(0, 0, 0, 1)"> )
}
}
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> withLoader</pre>
</div>
<div>
<div><span style="background-color: rgba(51, 153, 102, 1); color: rgba(255, 255, 255, 1)">功能是传入一个组件,在高阶组件中,有三个功能</span></div>
<div><span style="background-color: rgba(51, 153, 102, 1); color: rgba(255, 255, 255, 1)">1、发送传入的url请求,得到需要的data值;</span></div>
<div><span style="background-color: rgba(51, 153, 102, 1); color: rgba(255, 255, 255, 1)">2、有一定的判断逻辑,没有拿到数据的时候显示 <p>,否则显示 传入的组件</span></div>
<div><span style="background-color: rgba(51, 153, 102, 1); color: rgba(255, 255, 255, 1)">3、给传入的组件添加data值</span></div>
<div> </div>
<div>Vue中如何实现类似的功能呢?</div>
<div> </div>
<div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">父组件</span>
<template>
<Loading><DogImg/></Lading>
</template>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">子组件</span>
<template>
<div>
<p v-<span style="color: rgba(0, 0, 255, 1)">if</span>="load">loading...</p>
<div v-<span style="color: rgba(0, 0, 255, 1)">else</span>><slot><img src="/location.png"/></slot></div>
</div>
</template>
<script><span style="color: rgba(0, 0, 0, 1)">
{
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">需要传给父组件的data,使用 v-model 或者 vuex均可</span>
<span style="color: rgba(0, 0, 0, 1)"> }
</span></script></pre>
</div>
<p>PS:react父子组件使用vue中的slot</p>
<div class="cnblogs_code">
<pre>import React, { Component } from 'react'<span style="color: rgba(0, 0, 0, 1)">;
import Children from </span>'./Children'<span style="color: rgba(0, 0, 0, 1)">;
class Father extends Component {
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div>
<Children>
<em>1111111111111111</em>
</Children>
<p>我是父组件</p>
</div>
<span style="color: rgba(0, 0, 0, 1)">
)
}
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> Father;</pre>
</div>
<p>子组件</p>
<div class="cnblogs_code">
<pre>import React, { Component } from 'react'<span style="color: rgba(0, 0, 0, 1)">;
class Children extends Component{
constructor(props){
super(props)
}
render(){
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div><span style="color: rgba(0, 0, 0, 1)">
{</span><span style="color: rgba(0, 0, 255, 1)">this</span>.props.children <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">这里调用了其内部} </span>
<h1>我是子组件</h1>
</div>
<span style="color: rgba(0, 0, 0, 1)"> )
}
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> Children;</pre>
</div>
</div>
</div>
</div>
</div>
<div>首先定义 hooks/useURLLoader.tsx</div>
<div>
<div class="cnblogs_code">
<pre>import { useState, useEffect } from 'react'<span style="color: rgba(0, 0, 0, 1)">
import axios from </span>'axios'<span style="color: rgba(0, 0, 0, 1)">
const useURLLoader </span>= (url: string, deps: any[] = []) =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">第二个入参:deps,是决定该函数什么时候更新,因为放在了 useEffect 的第二个参数中</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">默认是空数组,也就是只执行一次</span>
const = useState<any>(<span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
const </span>= useState(<span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">)
useEffect(() </span>=><span style="color: rgba(0, 0, 0, 1)"> {
setLoading(</span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">)
axios.get(url).then(result </span>=><span style="color: rgba(0, 0, 0, 1)"> {
setData(result.data)
setLoading(</span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">)
})
}, deps)
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> useURLLoader</pre>
</div>
<div>然后在 App.jsx 中调用</div>
<div> </div>
<div>
<div class="cnblogs_code">
<pre>import useURLLoader from './hooks/useURLLoader'<span style="color: rgba(0, 0, 0, 1)">
interface IShowResult {
message: string;
status: string;
}
const App: React.FC </span>= () =><span style="color: rgba(0, 0, 0, 1)"> {
const </span>= useState(<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">)
const </span>= useURLLoader('http://www.xxx.com',);<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">每次show变化的时候,都要更新hooks中的useEffect函数</span>
const dogResult =<span style="color: rgba(0, 0, 0, 1)"> data as IShowResult;
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div className="App"><span style="color: rgba(0, 0, 0, 1)">
{
loading</span>?<p>图片下载中。。。</p>
:<img src={dogResult && dogResult.message}/>
<span style="color: rgba(0, 0, 0, 1)"> }
</span><p>
<button onClick={() => {setShow(!show)}}>Refresh dog photo</button>
</p>
</div>
<span style="color: rgba(0, 0, 0, 1)">);
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> App;</pre>
</div>
<p> </p>
</div>
</div>
<h4> useRef的使用</h4>
<div>
<p>先来看 state和props的每次改变,其实都相当于闭包,每次的数据都是独立的:</p>
<p>比如下面代码,点击了第一个button后,三秒后才弹出 like 的值;在此期间多次点击第二个按钮,当前的 like 已经是其他的数字,但是异步执行的时候,保留的仍然是当时的数值;</p>
<div class="cnblogs_code">
<pre>import React, { useState, useEffect } from 'react'<span style="color: rgba(0, 0, 0, 1)">
const LikeButton: React.FC </span>= () =><span style="color: rgba(0, 0, 0, 1)"> {
const </span>= useState(0<span style="color: rgba(0, 0, 0, 1)">)
useEffect(() </span>=><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>'document title effect is running'<span style="color: rgba(0, 0, 0, 1)">)
document.title </span>=<span style="color: rgba(0, 0, 0, 1)"> `点击了${like}次`
}, )
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> handleAlertClick() {
setTimeout(() </span>=><span style="color: rgba(0, 0, 0, 1)"> {
alert(</span>'you clicked on ' + <span style="color: rgba(0, 0, 0, 1)">like)
}, </span>3000<span style="color: rgba(0, 0, 0, 1)">)
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><>
<button onClick={() => {setLike(like + 1)}}><span style="color: rgba(0, 0, 0, 1)">
{like} 👍
</span></button>
<button onClick={handleAlertClick}> Alert!
</button>
</>
<span style="color: rgba(0, 0, 0, 1)">)
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> LikeButton</pre>
</div>
<p>所以使用useRef,注意的是修改ref的值并不会触发 render函数的更新,之所以函数更新是因为修改了 useState的值</p>
<div class="cnblogs_code">
<pre>import React, { useState, useEffect, useRef } from 'react'<span style="color: rgba(0, 0, 0, 1)">
const LikeButton: React.FC </span>= () =><span style="color: rgba(0, 0, 0, 1)"> {
const </span>= useState(0);<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">state在每一个render中都是独立的值,相当于闭包的存在</span>
const likeRef = useRef(0);<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">useRef 是一个函数,初始值是0;ref在所有的render中都保持的唯一的引用</span>
useEffect(() =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>'document title effect is running'<span style="color: rgba(0, 0, 0, 1)">)
document.title </span>=<span style="color: rgba(0, 0, 0, 1)"> `点击了${like}次`
}, )
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> handleAlertClick() {
setTimeout(() </span>=><span style="color: rgba(0, 0, 0, 1)"> {
alert(</span>'you clicked on ' + likeRef.current)<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">改动了这里,以current获取值</span>
}, 3000<span style="color: rgba(0, 0, 0, 1)">)
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><>
<button onClick={() => {setLike(like + 1;likeRef.current++)}}><span style="color: rgba(0, 0, 0, 1)">
{like} 👍
</span></button>
<button onClick={handleAlertClick}> Alert!
</button>
</>
<span style="color: rgba(0, 0, 0, 1)">)
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> LikeButton</pre>
</div>
<p> </p>
<div>
<div>前面useState只执行1次【模拟在生命周期:componentDidMount中执行】,是在第二个参数中,设置为[];</div>
<div>而如果useRef要求只执行1次【模拟在生命周期:componentDidMount中执行】,则设置标识位为false,改变后变为true,进行锁住;</div>
<div>这里实现的是,第一次不执行,待数据更新的时候再去执行</div>
<div> </div>
<div>
<div class="cnblogs_code">
<pre>import React, { useState, useEffect, useRef } from 'react'<span style="color: rgba(0, 0, 0, 1)">
const LikeButton: React.FC </span>= () =><span style="color: rgba(0, 0, 0, 1)"> {
const </span>= useState(0<span style="color: rgba(0, 0, 0, 1)">);
const didMountRef </span>= useRef(<span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">);
useEffect(()</span>=>{ <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">只有在数据更新的时候,才去执行</span>
<span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)">(didMountRef.current){
console.log(</span>'is update'<span style="color: rgba(0, 0, 0, 1)">);
}</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
didMountRef.current </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
}
})
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><>
<button onClick={() => {setLike(like + 1)}}><span style="color: rgba(0, 0, 0, 1)">
{like} 👍
</span></button>
<button onClick={handleAlertClick}> Alert!
</button>
</>
<span style="color: rgba(0, 0, 0, 1)">)
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> LikeButton</pre>
</div>
<p>useRef常用来访问节点:</p>
<div class="cnblogs_code">
<pre>import React, { useState, useEffect, useRef } from 'react'<span style="color: rgba(0, 0, 0, 1)">
const LikeButton: React.FC </span>= () =><span style="color: rgba(0, 0, 0, 1)"> {
const </span>= useState(0<span style="color: rgba(0, 0, 0, 1)">);
const domRef </span>= useRef<HTMLInputElement>(<span style="color: rgba(0, 0, 255, 1)">null</span>);<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">虽然初始化的时候是null,但是其实是DOM元素的范性</span>
useEffect(()=>{ <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">每次渲染render的时候后会去执行,一旦获取到dom元素,则获取光标,而用 useRef 定义的DOM元素,是唯一的</span>
<span style="color: rgba(0, 0, 255, 1)">if</span>(domRef &&<span style="color: rgba(0, 0, 0, 1)"> domRef.current){
domRef.current.focus()
}
})
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><>
<input type="text" ref = {domRef}/>
<button onClick={() => {setLike(like + 1)}}><span style="color: rgba(0, 0, 0, 1)">
{like} 👍
</span></button>
<button onClick={handleAlertClick}> Alert!
</button>
</>
<span style="color: rgba(0, 0, 0, 1)">)
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> LikeButton</pre>
</div>
<p> </p>
<p><strong>使用useContext解决数据多层透传的问题</strong></p>
<p>首先在最外层,父组件中使用:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> import React, { useState } from 'react'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> import LikeButton from './components/LikeButton'
<span style="color: rgba(0, 128, 128, 1)"> 3</span> import Hello from './components/Hello'
<span style="color: rgba(0, 128, 128, 1)"> 4</span> import useURLLoader from './hooks/useURLLoader'
<span style="color: rgba(0, 128, 128, 1)"> 5</span>
<span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 0, 0, 1)">interface IThemeProps {
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span> <span style="color: rgba(0, 0, 0, 1)">: {color: string; background: string;}
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span> const themes: IThemeProps =<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">10</span>'light'<span style="color: rgba(0, 0, 0, 1)">: {
</span><span style="color: rgba(0, 128, 128, 1)">11</span> color: '#000'<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)">12</span> background: '#eee'<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)">13</span> <span style="color: rgba(0, 0, 0, 1)"> },
</span><span style="color: rgba(0, 128, 128, 1)">14</span>'dark'<span style="color: rgba(0, 0, 0, 1)">: {
</span><span style="color: rgba(0, 128, 128, 1)">15</span> color: '#fff'<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)">16</span> background: '#222'<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)">17</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">18</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">19</span> export const ThemeContext = React.createContext(themes.light) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">定义context,且使用export导出</span>
<span style="color: rgba(0, 128, 128, 1)">20</span> const App: React.FC = () =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">21</span> const [ show, setShow ] = useState(<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">22</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">使用 ThemeContext.Provider 包裹所有的组件</span>
<span style="color: rgba(0, 128, 128, 1)">23</span> <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><span style="color: rgba(0, 128, 128, 1)">24</span> <div className="App">
<span style="color: rgba(0, 128, 128, 1)">25</span> <ThemeContext.Provider value={themes.dark}>
<span style="color: rgba(0, 128, 128, 1)">26</span> <header className="App-header">
<span style="color: rgba(0, 128, 128, 1)">27</span> <img src={logo} className="App-logo" alt="logo" />
<span style="color: rgba(0, 128, 128, 1)">28</span> <LikeButton />
<span style="color: rgba(0, 128, 128, 1)">29</span> <Hello />
<span style="color: rgba(0, 128, 128, 1)">30</span> </header>
<span style="color: rgba(0, 128, 128, 1)">31</span> </ThemeContext.Provider>
<span style="color: rgba(0, 128, 128, 1)">32</span> </div>
<span style="color: rgba(0, 128, 128, 1)">33</span> <span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">34</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">35</span>
<span style="color: rgba(0, 128, 128, 1)">36</span> export <span style="color: rgba(0, 0, 255, 1)">default</span> App;</pre>
</div>
<p> </p>
</div>
</div>
然后在子组件中调用:</div>
<div>
<div class="cnblogs_code">
<pre>import React, { useState, useEffect, useRef, useContext } from 'react'<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">导入useContext</span>
import { ThemeContext } from '../App' <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">自组件使用,首先要调用context</span>
const LikeButton: React.FC = () =><span style="color: rgba(0, 0, 0, 1)"> {
const theme </span>=<span style="color: rgba(0, 0, 0, 1)"> useContext(ThemeContext)
const style </span>=<span style="color: rgba(0, 0, 0, 1)"> {
background: theme.background,
color: theme.color,
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><>
<input type="text" ref={domRef} />
<button style={style}><span style="color: rgba(0, 0, 0, 1)">
{like} 👍
</span></button>
</>
<span style="color: rgba(0, 0, 0, 1)"> )
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> LikeButton</pre>
</div>
<p> </p>
<div>
<div>Hook规则:</div>
<div>1.只在最顶层使用Hook,不要在循环和条件语句中使用hook</div>
<div>2.只在react函数中调用hook</div>
<div> </div>
<div>Hooks 的相关网站:https://zh-hans.reactjs.org/docs/hooks-intro.html</div>
<div><em>Hook</em> 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。</div>
<div> </div>
</div>
</div><br><br>
来源:https://www.cnblogs.com/xiaozhumaopao/p/12636559.html
頁:
[1]