Typescript和React造轮子一之React hook基础(二)

754 阅读3分钟

第一个简单组件hello word

import React, { useContext } from 'react'
import { ThemeContext } from '../App'
interface IHelloProps {
  message?: string;
}

const Hello: React.FC<IHelloProps> = (props) => {
  const theme = useContext(ThemeContext)
  console.log(theme)
  const style = {
    background: theme.background,
    color: theme.color,
  }
  return <h2 style={style}>{props.message}</h2>
}
Hello.defaultProps = {
  message: "Hello World"
}

export default Hello
hook

钩子是允许从功能组件(function component)“挂钩”React状态和生命周期功能的功能。钩子在类内部不起作用 - 它们允许你在没有类的情况下使用React。

useState简单实例

import { useState } from 'react';

function Example() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

useEffect Effect Hook中的useEffect增加了在功能组件执行副作用的功能。它与React类中的componentDidMount,componentDidUpdate和componentWillUnmount具有相同的用途,但统一为单个API。 简单实例:

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

const MouseTracker: React.FC = () => {
  const [ positions, setPositions ] = useState({x: 0, y: 0})
  useEffect(() => {
    console.log('add effect', positions.x)
    const updateMouse= (e: MouseEvent) => {
      console.log('inner')
      setPositions({ x: e.clientX, y: e.clientY })
    }
    document.addEventListener('click', updateMouse)
    return () => {
      console.log('remove effect', positions.x)
      document.removeEventListener('click', updateMouse)
    }
  }, [])
  console.log('before render', positions.x)
  return (
    <p>X: {positions.x}, Y : {positions.y}</p>
  )
}

export default MouseTracker

useEffect不过不传递第二个参数在页面每次重新渲染时都会调用,这里第二个参数传入空数组的意思是只有在页面出现或者卸载时执行.如果传入其他变量则执行与否只依赖该变量是否变化.

不同组件之间共享hook封装

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

const useMousePosition = () => {
  const [ positions, setPositions ] = useState({x: 0, y: 0})
  useEffect(() => {
    console.log('add effect', positions.x)
    const updateMouse= (e: MouseEvent) => {
      setPositions({ x: e.clientX, y: e.clientY })
    }
    document.addEventListener('mousemove', updateMouse)
    return () => {
      console.log('remove effect', positions.x)
      document.removeEventListener('mousemove', updateMouse)
    }
  }, [])
  return positions
}

export default useMousePosition

定义后在其他组件中引入就可共用状态

const positons = useMousePosition();

自定义hook 在react hook中我们可以把一些天然逻辑重复的业务抽离出来,下面看下例子: 定义文件useURLLoader.tsx

import { useState, useEffect } from 'react'
import axios from 'axios'

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

export default useURLLoader

使用

interface IShowResult {
  message: string;
  status: string;
}

const App: React.FC = () => {
     const [show, setShow] = userState(true)
     const [data, loadin] = userState(true) = useURLLoader('https://dog.ceo/api/breeds/imagte', show)
     const dogResult = data as IShowResult 
    return (
        <div>
          <button oClick={() => {
           setShow(!show)
        }}>
           Refresh dog photo
          </button>
              
          {loading ? <p>读取中</p> : <imag src={dogResult && dogResult.message}  />}
        </div>
    )
}

useRef与useContext

App.tsx文件

import React, { useState } from 'react';
import './App.css';
import LikeButton from './components/LikeButton'

interface IThemeProps {
  [key: string]: {color: string; background: string;}
}
const themes: IThemeProps = {
 'light': {
   color: '#000',
   background: '#eee',
 },
 'dark': {
    color: '#fff',
    background: '#222',
  }
}
export const ThemeContext = React.createContext(themes.light)
const App: React.FC = () => {
  const [ show, setShow ] = useState(true)
  return (
    <div className="App">
      <ThemeContext.Provider value={themes.dark}>
      <header className="App-header">
        <LikeButton />
      </header>
      </ThemeContext.Provider>
    </div>
  );
}

export default App;

LikeButton.tsx文件

import React, { useState, useEffect, useRef, useContext } from 'react'
import { ThemeContext } from '../App'
const LikeButton: React.FC = () => {
  const [like, setLike] = useState(0)
  const likeRef = useRef(0)
  const didMountRef = useRef(false)
  const domRef = useRef<HTMLInputElement>(null)
  const theme = useContext(ThemeContext)
  console.log(theme)
  const style = {
    background: theme.background,
    color: theme.color,
  }
  useEffect(() => {
    console.log('document title effect is running')
    document.title = `点击了${like}次`
  }, [like])
  useEffect(() => {
    if (didMountRef.current) {
      console.log('this is updated')
    } else {
      didMountRef.current = true
    }
  })
  useEffect(() => {
    if (domRef && domRef.current) {
      domRef.current.focus()
    }
  })
  function handleAlertClick() {
    setTimeout(() => {
      alert('you clicked on ' + likeRef.current)
    }, 3000)
  }
  return (
    <>
    <input type="text" ref={domRef} />
    <button style={style} onClick={() => {setLike(like + 1); likeRef.current++}}>
      {like} 👍
    </button>
    <button onClick={handleAlertClick}> Alert!
    </button>
    </>
  )
}
export default LikeButton