# react和typescript编写程序

+ useState
+ useEffect
+ usexxxx自定义hook
+ useMemo
+ useCallback

# npx

npm 从5.2版开始,增加了 npx 命令。

 npx create-react-app tsui --typescript

# react的第一个ts组件

{/*父组件*/}
import React from 'react';
import logo from './logo.svg';
import Hello from "./components/Hello"
import './App.css';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.tsx</code> and save to reload.
        </p>
		<Hello message="welcome2221"/>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

export default App;
import React from "react"
interface P{
	message:string	
}
const Hello = (props:P) =>{
	return <h2>{props.message}</h2>
}

export default Hello

import React from "react"
interface P{
	message?:string	
}
const Hello:React.FC<P> = (props) =>{
	return <h2>{props.message}</h2>
}
Hello.defaultProps={
	message:"ssss"
}
export default Hello


{/*React.FC=React.FunctionComponent,FC自带扩展很多其他属性*/}

# react的calss类型组件和函数类型组件

函数类型组件不能直接的使用state和生命周期的函数

# react-hook

Hook 是一些可以让在函数组件里“钩入” React state 及生命周期等特性的函数。Hook 不能在 class 组件中使用

// if(xxx) return xxx // 组件不能在这里直接处理了

hooks()
if(xxx) return xxx
return hooks something

使用了hooks,分支判断是要注意位置,如果在hooks之前走了if(xxx),可能报错:Rendered fewer hooks than expected. This may be caused by an accidental early return statement.”

# useState

useState返回数组,第一个是当前的值,第二个是更新state的函数,函数名可自定义。

副作用

副作用是相对于主作用来说的,一个函数除了主作用,其他的作用就是副作用。对于 React 组件来说,主作用就是根据数据(state/props)渲染 UI,除此之外都是副作用(比如,手动修改 DOM);

比如函数:

let f=1
function fun(a,b){
	f++
	return a+b
}
操作了f,就是触发了副作用

Effect Hook 可以让在函数组件中执行副作用操作。数据获取,设置订阅以及手动更改 React 组件中的 DOM 都属于副作用

useState不能嵌套在if/for/其它函数中(react按照hooks的调用顺序识别每一个hook)

第二个参数比如叫setCount是一个函数,它里面的参数表示最新的状态值;这样就可以实现更新,修改状态的时候,一定要使用新的状态替换旧的状态,不能直接修改旧的状态,尤其是引用类型

import React ,{useState} from "react"
const LikeButton:React.FC = () =>{
	const [like,setLike] = useState(0)
	
	return (
		<button onClick={()=>{setLike(like+1)}}>
			{like}</button>
	)
}
export default LikeButton
import React ,{Component} from "react"

class LikeButton extends Component<{},{like:any}>{
	constructor(props:any) {
 	  super(props)
 		this.state={
 			like:0
 		}
 	}
	render(){
		return(
			<button onClick={this.setlike.bind(this)}>
				{this.state.like}</button>
		)
	}
	setlike(){
		console.log(this.state.like)
		// let tem=this.state.like
		this.setState({
			like:this.state.like+1
		})
	}
}

export default LikeButton
import React ,{useState} from "react"
const LikeButton:React.FC = () =>{
	const [obj,setObj] = useState({like:0,on:true})
	
	return (
		<>
		<button onClick={()=>{setObj({like:obj.like+1,on:obj.on})}}>
			{obj.like}</button>
		<button onClick={()=>{setObj({like:obj.like,on:!obj.on})}}>
			{obj.on?"ON":"OFF"}
		</button>
		</>
	)
}
export default LikeButton
import React ,{useState} from "react"
const LikeButton:React.FC = () =>{
	const [like,setLike] = useState(0)
	const [on,setOn] = useState(true)
	return (
		<>
		<button onClick={()=>{setLike(like+1)}}>
			{like}</button>
		<button onClick={()=>{setOn(!on)}}>
			{on?"ON":"OFF"}
		</button>
		</>
	)
}
export default LikeButton

在函数组件主体内(这里指在 React 渲染阶段)改变 DOM、添加订阅、设置定时器、记录日志以及执行其他包含副作用的操作都是不被允许的,因为这可能会产生莫名其妙的 bug 并破坏 UI 的⼀致性。

使用 useEffect 完成副作用操作。赋值给 useEffect 的函数会在组件渲染到屏幕之后执行。你可以把 effect 看作从 React 的纯函数式世界通往命令式世界的逃生通道。

默认情况下,effect 将在每轮渲染结束后执行,但你可以选择让它 在只有某些值改变的时候 才执行。

》 可以更新多个 state 变量——甚至来自多个组件的 state 变量——而不会触发太多的 重新渲染。但这也意味着只有在你的事件处理函数及其中任何代码执行完成 之后,UI 才会更新。这种特性也就是 批处理

一个函数组件可以有多个usestate

# useState回调函数的参数

这是一种告诉 React “用 state 值做某事”而不是仅仅替换它的方法。

如果初始 state 需要通过计算才能获得,则可以传入一个函数,在函数中计算并返回初始的 state,此函数只在初始渲染时被调用

import { useState } from 'react'

function Counter(props) {
  const [count, setCount] = useState(() => {
	// 当然也可以不借助函数而直接useState(props.count)
	// 只是为了展示回调函数的用法举例而已
    return props.count
  })
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>{count}</button>
    </div>
  )
}

function App() {
  return (
    <>
      <Counter count={10} />
      <Counter count={20} />
    </>
  )
}

export default App

const [score, setScore]; = useState(0); setScore(s => s + 1)多次出发数据都会更新,而setScore(score+1)这种和根据场景合并操作,导致非预期结果

setNumber(n => n + 1);

在这里,n => n + 1 被称为 更新函数。当你将它传递给一个 state 设置函数时:

React 会将此函数加入队列,以便在事件处理函数中的所有其他代码运行后进行处理。 在下一次渲染期间,React 会遍历队列并给你更新之后的最终 state。

import { useState } from 'react';
export default function Counter() {
  const [number, setNumber] = useState(0);
  return (
    <>
      <h1>{number}</h1>
      <button onClick={() => {
        setNumber(number + 5);
        setNumber(n => n + 1);
        setNumber(42);
      }}>增加数字</button>
    </>
  )
}

以下是 React 在执行事件处理函数时处理这几行代码的过程:

  • setNumber(number + 5):number 为 0,所以 setNumber(0 + 5)。React 将 “替换为 5” 添加到其队列中。
  • setNumber(n => n + 1):n => n + 1 是一个更新函数。React 将该函数添加到其队列中。
  • setNumber(42):React 将 “替换为 42” 添加到其队列中。 在下一次渲染期间,React 会遍历 state 队列:
更新队列 n 返回值
“替换为 5” 0(未使用) 5
n => n + 1 5 5 + 1 = 6
“替换为 42” 6(未使用) 42

然后 React 会保存 42 为最终结果并从 useState 中返回。

# useEffect

effect 的条件执行:默认情况下,effect 会在每轮组件渲染完成后执行。这样的话,一旦 effect 的依赖发生变化,它就会被重新创建。

然而,在某些场景下这么做可能会矫枉过正。比如不需要在每次组件更新时都创建新的订阅,⽽是仅需要在某些内容改变时重新创建。要实现这一点,可以给 useEffect 传递第二个参数,它是 effect 所依赖的值数组。更新后的示例如下:

import React, { useState, useEffect } from "react";
export default function HookPage(props) {
 // 声明⼀个叫 “count” 的 state 变量,初始化为0
 const [count, setCount] = useState(0);
 const [date, setDate] = useState(new Date());
 // 与 componentDidMount 和 componentDidUpdate相似
 useEffect(() => {
	// 更新 title
	document.title = `You clicked ${count} times`;
}, [count]);
useEffect(() => {
	const timer = setInterval(() => {
	setDate(new Date());
}, 1000);
}, []);
	return (
		<div>
		<h3>HookPage</h3>
		<p>{count}</p>
		<button onClick={() => setCount(count + 1)}>add</button>
		<p>{date.toLocaleTimeString()}</p>
		</div>
 	);
}

此时,只有当 useEffect第二个参数数组里的数值 改变后才会重新创建订阅。

# 依赖项控制执行时机

  1. 不添加依赖项

渲染和更新

  • 组件首次渲染执行一次,以及不管是哪个状态更改引起组件更新时都会重新执行
  • 组件初始渲染
  • 组件更新 (不管是哪个状态引起的更新)
useEffect(()=>{
    console.log('副作用执行了')
})
  1. 添加空数组:组件只在首次渲染时执行一次
useEffect(()=>{
	 console.log('副作用执行了')
},[])
  1. 添加特定依赖项:首次渲染时执行,然后在a或者b变化时执行
useEffect(()=>{
	 console.log('副作用执行了')
},[a,b])

如果useEffect中使用了count1,则需要依赖中添加count1,否则可能会有bug;

React Hook useEffect has a missing dependency: 'count1'. Either include it or remove the dependency array.

# 清除effect

通常,组件卸载时需要清除 effect 创建的诸如订阅或计时器 ID 等资源。要实现这一点, useEffect函数需返回一个清除函数,以防⽌内存泄漏,清除函数会在组件卸载前执行。

useEffect(() => {
 const timer = setInterval(() => {
 setDate(new Date());
 }, 1000);
 return () => clearInterval(timer);
}, []);
import React ,{useState,useEffect} from "react"
const LikeButton:React.FC = () =>{
	const [like,setLike] = useState(0)
	const [on,setOn] = useState(true)
	useEffect(()=>{
		document.title=`${like}`
	})
	return (
		<>
		<button onClick={()=>{setLike(like+1)}}>
			{like}</button>
		<button onClick={()=>{setOn(!on)}}>
			{on?"ON":"OFF"}
		</button>
		</>
	)
}
export default LikeButton
import React ,{useState,useEffect} from "react"
const MouseTracker:React.FC = () =>{
	const [position ,setPosition] =useState({x:0,y:0})
	useEffect(()=>{
		const update=(e:MouseEvent)=>{
			setPosition({x:e.clientX,y:e.clientY})
		}
	
		document.addEventListener("click",update)
		return ()=>{
			
			document.removeEventListener("click",update)
		}
	})
	
	return (
		<p>x:{position.x},y:{position.y}</p>
	)
}
export default  MouseTracker

警告

useEffect处理监听事件定时器之类的需要回收移除,useEffect自己会返回一个函数,可以在里面进行对应的操作。同时可以给e指定类型

如果频繁触发这些监听移除没有必要,useEffect的第二个参数就是一个数组,可以填入需要每次更改的state的值,其他的则默认不去进行改动

import React ,{useState,useEffect} from "react"
const MouseTracker:React.FC = () =>{
	const [position ,setPosition] =useState({x:0,y:0})
	console.log(0)
	useEffect(()=>{
		
		const update=(e:MouseEvent)=>{
			console.log(5)
			setPosition({x:e.clientX,y:e.clientY})
		}
		console.log(1)
		document.addEventListener("click",update)
		return ()=>{
			console.log(2)
			document.removeEventListener("click",update)
		}
	},[])
	{/*}加了空数组就不会持续触发1,2,每次点击只会执行5,0,3这几个值*/}
	console.log(3)
	return (
		<p>x:{position.x},y:{position.y}</p>
	)
}
export default  MouseTracker

# useMemo

把“创建”函数依赖项数组作为参数传⼊ useMemo ,它仅会在某个依赖项改变时才重新计算memoized值。这种优化有助于避免在每次渲染时都进行高开销的计算。

import React, { useState, useMemo } from "react";
export default function UseMemoPage(props) {
	const [count, setCount] = useState(0);
	const expensive = useMemo(() => {
		console.log("compute");
		let sum = 0;
		for (let i = 0; i < count; i++) {
		sum += i;
		}
		return sum;
		//只有count变化,这⾥才重新执⾏
	}, [count]);
	const [value, setValue] = useState("");
	return (
	<div>
	<h3>UseMemoPage</h3>
	<p>expensive:{expensive}</p>
	<p>{count}</p>
	<button onClick={() => setCount(count + 1)}>add</button>
	<input value={value} onChange={event => setValue(event.target.value)} />
	</div>
 );
}
import React, { useState, useMemo } from 'react';
 
function Info(props) {
  let [personalInfo , setPersonalInfo] = useState({
    name: 'kevin kang',
    gender: 'male'
  })

  let [val,setVal] =useState(0)
 
  function formatGender(gender) {
    console.log('---调用了翻译性别的方法---')
    return gender === 'male' ? '男' : '女'
  }
  
  function setValFun(){
    setVal(val+1)
  }
 
  // BAD 
  // 不使用useMemo的情况下,修改其他属性,也会重新调用formatGender方法,浪费计算资源
  // let gender =  formatGender(personalInfo.gender)
 
  // GOOD
  let gender = useMemo(()=>{
    return formatGender(personalInfo.gender)
  }, 
  [personalInfo.gender])
 
  return (
    <>
        <div>
          姓名: {personalInfo.name} -- 性别:  { gender } <br/>
          <button onClick={ 
            ()=> { 
              setPersonalInfo({
                ...personalInfo,
                name: 'Will Kang'
              }) 
            }  
          }> 点击修改名字</button>
        </div>
        <div>
          {val}
          <button onClick={setValFun}>click</button>  
        </div>
    </>
  )
}
 
export default Info

# useCallback

把内联回调函数及依赖项数组作为参数传⼊ useCallback ,它将返回该回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新。当把回调函数传递给经过优化的并使⽤引用相等性去避免非必要渲染(例如 shouldComponentUpdate )的子组件时,它将非常有用。

import React, { useState, useCallback, PureComponent } from "react";
export default function UseCallbackPage(props) {
 const [count, setCount] = useState(0);
 const addClick = useCallback(() => {
	let sum = 0;
	for (let i = 0; i < count; i++) {
		sum += i;
 	}
 	return sum;
 }, [count]);
 const [value, setValue] = useState("");
 return (
 <div>
	<h3>UseCallbackPage</h3>
	<p>{count}</p>
	<button onClick={() => setCount(count + 1)}>add</button>
	<input value={value} onChange={event => setValue(event.target.value)} />
	<Child addClick={addClick} />
 </div>
 );
}
class Child extends PureComponent {
 render() {
 console.log("child render");
 const { addClick } = this.props;
 return (
	<div>
		<h3>Child</h3>
		<button onClick={() => console.log(addClick())}>add</button>
	</div>
 );
 }
}
  • 打印函数中的timer,有缓存的timer值一直不会变,除非useCallback触发或者页面重新渲染
  • 而不缓存的方法会每次都会触发
import React, { useCallback, useEffect, useState } from "react";

export default function App() {
  const [count, setCount] = useState(0);
  let timer =new Date().valueOf()
  console.log(timer,1)
  // 使用 useCallBack 缓存
  const handleCountAddByCallBack = useCallback(() => {
    console.log(timer,2)
    setCount((count) => count + 1);
  }, []);

  // 不缓存,每次 count 更新时都会重新创建
  const handleCountAdd = () => {
    console.log(timer,3)
    setCount((count) => count + 1);
  };

  return (
    <div className="App">
      <h3>CountAddByChild1: {count}</h3>
      <Child1 addByCallBack={handleCountAddByCallBack} add={handleCountAdd} />
    </div>
  );
}

const Child1 = React.memo(function (props) {
  const { add, addByCallBack } = props;
  
  // 没有缓存,由于每次都创建,memo 认为两次地址都不同,属于不同的函数,所以会触发 useEffect
  useEffect(() => {
    console.log("Child1----addFcUpdate", props);
  }, [add]);

  // 有缓存,memo 判定两次地址都相同,所以不触发 useEffect
  useEffect(() => {
    console.log("Child1----addByCallBackFcUpdate", props);
  }, [addByCallBack]);

  return (
    <div>
      <button onClick={props.add}>+1</button>
      <br />
      <button onClick={props.addByCallBack}>+1(addByCallBack)</button>
    </div>
  );
});

useCallback(fn, deps) 相当于 useMemo(() => fn, deps) 。

useMomo 和useCallback

useMemo 和 useCallback 接收的参数都是一样,第一个参数为回调 第二个参数为要依赖的数据

  • 共同作用:

    1. 仅仅 依赖数据 发生变化, 才会重新计算结果,也就是起到缓存的作用。
  • 两者区别:

    1. useMemo 计算结果是 return 回来的值, 主要用于 缓存计算结果的值 ,应用场景如: 需要 计算的状态
    2. useCallback 计算结果是 函数, 主要用于 缓存函数,应用场景如: 需要缓存的函数,因为函数式组件每次任何一个 state 的变化 整个组件 都会被重新刷新,一些函数是没有必要被重新刷新的,此时就应该缓存起来,提高性能,和减少资源浪费。

# useReducer

在组件中调用useReducer:在组件函数内部,调用useReducer并传入reducer函数和状态的初始值。useReducer返回一个数组,第一个元素是当前的状态,第二个元素是一个dispatch函数,用于分发动作。

  
import React, { useReducer } from 'react';  

function reducer(state, action) {  
  switch (action.type) {  
    case 'increment':  
      return { count: state.count + 1 };  
    case 'decrement':  
      return { count: state.count - 1 };  
    default:  
      throw new Error();  
  }  
}

export default function Counter() {  
  const [state, dispatch] = useReducer(reducer, { count: 0 });  
  return (  
    <div>  
      <p>Count: {state.count}</p>  
      <button onClick={() => dispatch({ type: 'increment' })}>Increment</button>  
      <button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>  
    </div>  
  );  
}

# react自定义Hook

有时候会想要在组件之间重用一些状态逻辑。目前为止,有两种主流方案来解决这个问题:高阶组件和 render props。自定义Hook可以在不增加组件的情况下达到同样的目的。

自定义 Hook 是一个函数,其名称以 “use” 开头,函数内部可以调用其他的 Hook。

可以把功能封装好,然后再项目中引用即可

import React, { useState, useEffect, useMemo } from "react";
export default function CustomHookPage(props) {
	//定义⼀个叫count的state变量,初始化为0
	const [count, setCount] = useState(0);
	//和didMount、didUpdate类似
	useEffect(() => {
	console.log("count effect");
	// 只需要在count发生改变的时候执行就可以啦
	document.title = `点击了${count}`;
	}, [count]);
	return (
		<div>
			<h3>自定义Hook</h3>
			<p>{count}</p>
			<button onClick={() => setCount(count + 1)}>add</button>
			<p>{useClock().toLocaleTimeString()}</p>
		</div>
	);
	}
	//自定义hook,命名必须以use开头
	function useClock() {
	const [date, setDate] = useState(new Date());
	useEffect(() => {
	console.log("date effect");
	//只需要在didMount时候执行就可以了
	const timer = setInterval(() => {
		setDate(new Date());
	}, 1000);
	//清除定时器,类似willUnmount
	return () => clearInterval(timer);
	}, []);
	return date;
}
  • 自定义hook函数,可以自动同步到本地LocalStorage
import { useEffect, useState } from 'react'

export function useLocalStorage (key, defaultValue) {
  const [message, setMessage] = useState(defaultValue)
  // 每次只要message变化 就会自动同步到本地ls
  useEffect(() => {
    window.localStorage.setItem(key, message)
  }, [message, key])
  return [message, setMessage]
}

const [message, setMessage] = useLocalStorage(key,defaultValue)

hook使用规则

Hook就是JS函数,但是使用它们会以下几个额外的规则:

  • 只能在函数最外层调用Hook。不要在循环、条件判断或者子函数中调用。
  • 只能在React的函数组件中调用 Hook。不要在其他JS函数中调用。(还有一个地方可以调用Hook —— 就是自定义的 Hook 中。)
  • 自定义hook,命名必须以use开头
import  {useState,useEffect} from "react"
const usePosition= () =>{
	const [position ,setPosition] =useState({x:0,y:0})

	useEffect(()=>{
		
		const update=(e:MouseEvent)=>{
			
			setPosition({x:e.clientX,y:e.clientY})
		}
		
		document.addEventListener("mousemove",update)
		return ()=>{
			
			document.removeEventListener("mousemove",update)
		}
	},[])
	
	return position
}
export default usePosition
{/*app.tsx*/}
import React from 'react';
import logo from './logo.svg';
import MouseTracker from "./components/MouseTracker"
import usePosition from "./hooks/usePosition"
import './App.css';

function App() {
	const position=usePosition()
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
			x:{position.x},y:{position.y}
        </p>
		<MouseTracker/>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}
export default App;

# 自定义hook异步读取一张图

import axios from "axios"
import  {useState,useEffect} from "react"
const useURLLoader= (url:string,deps:any[]=[]) =>{
	const[data,setData] =useState<any>(null)
	const[loading,setLoading]=useState(false)
	
	useEffect(()=>{
		setLoading(true)
		axios.get(url).then(res=>{
			setData(res.data)
			setLoading(false)
		})
	},deps)

	return [data,loading]
	
	
}
export default useURLLoader

import React from 'react';
import useURLLoader from "./hooks/useURLLoader"
import './App.css';
interface IshowResult{
	message:string;
	status:string
}
function App() {
	const [data,loading] =useURLLoader("https://dog.ceo/api/breeds/image/random")
	const dogResult =data as IshowResult
  return (
    <div className="App">
		{loading?<p>狗读取中</p>:<img src={dogResult && dogResult.message}/>}
    </div>
  );
}
export default App;

# useRef

useRef在react中返回值的类型定义:

 interface MutableRefObject<T> {
        current: T;
  }

可以看到useRef返回值是一个包括属性current类型为范型<T>的一个object。

它与直接在function compoent中定义一个{ current:xxx }的区别就是。

useRef会在所有的render中保持对返回值的唯一引用。因为所有对ref的赋值和取值拿到的都是最终的状态,并不会因为不同的render中存在不同的隔离。简单来说,可以将useRef的返回值,想象成为一个全局变量

import React ,{useState,useEffect,useRef} from "react"
const LikeButton:React.FC = () =>{
	const [like,setLike] = useState(0)
	const [on,setOn] = useState(true)
	let uselike = useRef(0)
	useEffect(()=>{
		document.title=`${like}`
	})
	function handleAlertClick(){
		setTimeout(()=>{
			console.log(uselike.current)
		},3000)
	}
	return (
		<>
		<button onClick={()=>{setLike(like+1);uselike.current++}}>
			{like}</button>
		<button onClick={()=>{setOn(!on)}}>
			{on?"ON":"OFF"}
		</button>
		<button onClick={handleAlertClick}>xxx</button>
		</>
	)
}
export default LikeButton

如果获取的是like,他不会是实时值,而是之前点击时的时候值,使用useRef可以获取实时值可以与dom结合使用

import React, { useState, useEffect, useRef } from 'react';

export default function Demo() {
  const [like, setLike] = useState(0);
  const likeRef = useRef(0);

  useEffect(() => {});

  const handleClick = () => {
    setLike(like + 1);
    likeRef.current = likeRef.current + 1;
  };

  const getLikeValue = () => {
    setTimeout(() => {
      alert(likeRef.current);
    }, 2000);
  };

  return (
    <div>
      <button onClick={handleClick}>+</button>
      <button>{like} 👍</button>
      <button onClick={getLikeValue}>获得like值</button>
    </div>
  );
}

既修改了state值,又修改了useRef的值,alert弹出时使用的是useRef的current

当然需要额外注意的是,修改useRef返回的值并不会引起react进行重新渲染执行函数,demo中的页面渲染不是因为修改Ref的值,而是因为我们在修改likeRef.current时同时修改了state中的setLike造成了页面渲染。

useRef的值改变并不会造成页面重新渲染,这一点可以做很多事情。比如可以配合useEffect查询页面是首次渲染还是更新

  1. 导入 useRef 函数
  2. 执行 useRef 函数并传入null,返回值为一个对象 内部有一个current属性存放拿到的dom对象(组件实例)
  3. 通过ref 绑定 要获取的元素或者组件
import { useEffect, useRef } from 'react'
function App() {  
    const h1Ref = useRef(null)  
    useEffect(() => {    
        console.log(h1Ref)  
    },[])  
    return (    
        <div>      
            <h1 ref={ h1Ref }>this is h1</h1>    
        </div>  
    )
}
export default App

总结

  • 可以将useRef返回值看作一个组件内部全局共享变量,它会在渲染内部共享一个相同的值。相对state/props他们是独立于不同次render中的内部作用域值。
  • 同时额外需要注意useRef返回值的改变并不会引起组件重新render,这也是和state/props不同的地方。
  • 当然我们在React.functionComponent中想要获取对应jsx的真实Dom元素时候也可以通过useRef进行获取到对应的Dom元素。

# useContext

组件中共享,比如在做换肤的时候就可以使用

# 实现步骤

  1. 使用createContext 创建Context对象
  2. 在顶层组件通过Provider 提供数据
  3. 在底层组件通过useContext函数获取数据
import React from 'react';
import logo from './logo.svg';
import MouseTracker from "./components/MouseTracker"
import LikeButton from "./components/LikeButton"
import usePosition from "./hooks/usePosition"

import useURLLoader from "./hooks/useURLLoader"
import './App.css';
interface IshowResult{
	message:string;
	status:string
}
interface IThemeProps{
	[key:string]:{color:string;background:string}
}
const themes:IThemeProps={
	'light':{
		color:"orange",
		background:"#eee"
	},
	'dark':{
		color:"#FFF",
		background:"#222"
	}
}
export const ThemeContext =React.createContext(themes.light)
console.log(ThemeContext)
function App() {
	const position=usePosition()
	const [data,loading] =useURLLoader("https://dog.ceo/api/breeds/image/random")
	const dogResult =data as IshowResult
  return (
    <div className="App">
	<ThemeContext.Provider value={themes.light}>
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
			x:{position.x},y:{position.y}
        </p>
		{loading?<p>狗读取中</p>:<img src={dogResult && dogResult.message}/>}
		<MouseTracker/>
		<LikeButton/>
		
      </header>
	  </ThemeContext.Provider>
    </div>
  );
}

export default App;
import React ,{useState,useEffect,useRef,useContext} from "react"
import {ThemeContext} from "../App"
const LikeButton:React.FC = () =>{
	const [like,setLike] = useState(0)
	const [on,setOn] = useState(true)
	let uselike = useRef(0)
	const theme =useContext(ThemeContext)
	
	const style={
		background:theme.background,
		color:theme.color
	}
	useEffect(()=>{
		document.title=`${like}`
	})
	function handleAlertClick(){
		setTimeout(()=>{
			alert(uselike.current)
		},3000)
	}
	return (
		<>
		<button onClick={()=>{setLike(like+1);uselike.current++}} style={style}>
			{like}</button>
		<button onClick={()=>{setOn(!on)}}>
			{on?"ON":"OFF"}
		</button>
		<button onClick={handleAlertClick}>xxx</button>
		</>
	)
}
export default LikeButton

import { createContext, useContext } from 'react'
// 创建Context对象
const Context = createContext()

function Foo() {  
    return <div>Foo <Bar/></div>
}

function Bar() {  
    // 底层组件通过useContext函数获取数据  
    const name = useContext(Context)  
    return <div>Bar {name}</div>
}

function App() {  
    return (    
        // 顶层组件通过Provider 提供数据    
        <Context.Provider value={'this is name'}>     
            <div><Foo/></div>    
        </Context.Provider>  
    )
}

export default App

context

context机制的数据查找链 Provider如果找不到 就找createContext方法执行时传入的参数

const context = React.createContext(rootStore) 
最后更新: 2/27/2025, 1:20:34 PM