博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
React 自定义Hook的思想
阅读量:4085 次
发布时间:2019-05-25

本文共 10920 字,大约阅读时间需要 36 分钟。

阅读前提:希望你对react-hook的官方文档有几次阅读。

下面我基本是拿了人家的代码直接过来自己执行。该骂看起来不难,重要的是人家的思路值得学习。 如果想看原味的请移步这里,仔细看完你会觉得太美了。

下面的代码是封装类似于useHttp的自定义hook。

useState基本使用方法

import React, { useState } from 'react';function App() {  const [data, setData] = useState({ hits: [] });  return (    
);}export default App;复制代码

初始化异步的axios请求

axios最基本的使用

import React, { useState, useEffect } from 'react';import axios from 'axios’;function App() {  const [data, setData] = useState({ hits: [] });  useEffect(async () => {    const result = await axios(      'http://hn.algolia.com/api/v1/search?query=redux',    );    setData(result.data);  });  return (    
);}export default App;复制代码

只要组件渲染一次 useEffect就会重新执行一次 会循环发送请求

可以替代原来react生命周期的componentDidMount

useEffect(async () => {  const result = await axios(    'http://hn.algolia.com/api/v1/search?query=redux',  );  setData(result.data);}, []);  // 每次传递一个空数组  只有第一次初始化组件的时候 才会渲染复制代码

组件执行的时候会报错 因为 useEffect不支持async异步的函数

修改之后

useEffect(() => {  const fetchData = async () => {    const result = await axios(      'http://hn.algolia.com/api/v1/search?query=redux',    );    setData(result.data);  };  fetchData();}, []);复制代码

 

hook函数已经替代componentDidMount这个生命周期函数钩子

 

如何去触发hook这个钩子函数

我们使用input的输入 替代state的变化 进行触发useEffect函数钩子

query发生变化的时候 自动发送请求

import React, { Fragment, useState, useEffect } from 'react';import axios from 'axios';function App() {  const [data, setData] = useState({ hits: [] });  const [query, setQuery] = useState('redux');  useEffect(() => {    const fetchData = async () => {      const result = await axios(        'http://hn.algolia.com/api/v1/search?query=redux',      );      setData(result.data);    };    fetchData();  }, []);  return (    
setQuery(event.target.value)} />
);}export default App;复制代码

 

useEffect现在只有初始化的时候执行一次

useEffect(() => {  const fetchData = async () => {    const result = await axios(      `http://hn.algolia.com/api/v1/search?query=${query}`,    );    setData(result.data); },[])复制代码

现在 当我们在input输入文字的时候 需要触发这个钩子函数 useEffect的第二个参数 中的state发生变化的时候 就会自动触发该钩子函数重新执行一次

useEffect(() => {  const fetchData = async () => {    const result = await axios(      `http://hn.algolia.com/api/v1/search?query=${query}`,    );    setData(result.data); },[query])     // 添加需要触发这个钩子的state复制代码

只要query发生变化。这个函数就会重新执行 现在已经实现每次输入一个字符 就会发送一个请求

接下来需要 添加一个按钮 去获取input输入的字符 然后点击按钮的时候去触发useEffect

function App() {  const [data, setData] = useState({ hits: [] });  const [query, setQuery] = useState('redux’);  const [search, setSearch] = useState('');  useEffect(() => {    const fetchData = async () => {      const result = await axios(        `http://hn.algolia.com/api/v1/search?query=${query}`,      );      setData(result.data);    };    fetchData();  }, [query]);  return (    
setQuery(event.target.value)} />
);}复制代码

 

每次input输入的时候 会触发query的变化。button点击的时候 将query设置成 search

useEffect(() => {  const fetchData = async () => {    const result = await axios(      `http://hn.algolia.com/api/v1/search?query=${search}`,    );    setData(result.data);  };  fetchData();}, [search]);复制代码

现在只有search发生变化的时候 才会触发useEffect触发 search要触发的话 只有点击button的时候才能获取

此处将URL单独提取出来

function App() {  const [data, setData] = useState({ hits: [] });  const [query, setQuery] = useState('redux');  const [url, setUrl] = useState(    'http://hn.algolia.com/api/v1/search?query=redux',  );  useEffect(() => {    const fetchData = async () => {      const result = await axios(url);      setData(result.data);    };    fetchData();  }, [url]);  return (    
setQuery(event.target.value)} />
);}复制代码

 

在hook中添加loadding

请求之前进行loadding的设置 并且渲染到ui中

import React, { Fragment, useState, useEffect } from 'react';import axios from 'axios';function App() {  const [data, setData] = useState({ hits: [] });  const [query, setQuery] = useState('redux');  const [url, setUrl] = useState(    'http://hn.algolia.com/api/v1/search?query=redux',  );  const [isLoading, setIsLoading] = useState(false);  useEffect(() => {    const fetchData = async () => {      setIsLoading(true);      const result = await axios(url);      setData(result.data);      setIsLoading(false);    };    fetchData();  }, [url]);  return (    
setQuery(event.target.value)} />
{isLoading ? (
Loading ...
) : (
)}
);}export default App;复制代码

 

HOOK中添加捕获错误的显示组件

import React, { Fragment, useState, useEffect } from 'react';import axios from 'axios';function App() {  const [data, setData] = useState({ hits: [] });  const [query, setQuery] = useState('redux');  const [url, setUrl] = useState(    'http://hn.algolia.com/api/v1/search?query=redux',  );  const [isLoading, setIsLoading] = useState(false);  const [isError, setIsError] = useState(false);  useEffect(() => {    const fetchData = async () => {      setIsError(false);      setIsLoading(true);      try {        const result = await axios(url);        setData(result.data);      } catch (error) {        setIsError(true);      }      setIsLoading(false);    };    fetchData();  }, [url]);  return (    
setQuery(event.target.value)} />
{isError &&
Something went wrong ...
} {isLoading ? (
Loading ...
) : (
)}
);}export default App;复制代码

 

使用form表单进行提交表单

setUrl(`http://hn.algolia.com/api/v1/search?query=${query}`) } >
setQuery(event.target.value)} />
{isError &&
Something went wrong ...
} …
复制代码

我们需要阻止默认的表单提交动作

{ setUrl(`http://hn.algolia.com/api/v1/search?query=${query}`); event.preventDefault();}}>复制代码

自定义HOOK 将hook的动作提取出来进行单独的封装调用

const useHackerNewsApi = () => {  const [data, setData] = useState({ hits: [] });  const [url, setUrl] = useState(    'http://hn.algolia.com/api/v1/search?query=redux',  );  const [isLoading, setIsLoading] = useState(false);  const [isError, setIsError] = useState(false);  useEffect(() => {    const fetchData = async () => {      setIsError(false);      setIsLoading(true);      try {        const result = await axios(url);        setData(result.data);      } catch (error) {        setIsError(true);      }      setIsLoading(false);    };    fetchData();  }, [url]);  return [{ data, isLoading, isError }, setUrl];}复制代码

使用useHackerNewsApi

function App() {  const [query, setQuery] = useState('redux');  const [{ data, isLoading, isError }, doFetch] = useHackerNewsApi();  return (    
{ doFetch(`http://hn.algolia.com/api/v1/search?query=${query}`); event.preventDefault(); }}>
setQuery(event.target.value)} />
...
);}复制代码

 

此时还没有初始化请求的url 通过传递参数 进行初始化请求参数

function App() {  const [query, setQuery] = useState('redux');  const [{ data, isLoading, isError }, doFetch] = useDataApi(    'http://hn.algolia.com/api/v1/search?query=redux',    { hits: [] },   );...const useDataApi = (initialUrl, initialData) => {  const [data, setData] = useState(initialData);  const [url, setUrl] = useState(initialUrl);...复制代码

 

使用Reducer Hook来提取数据

在useEffect中多次设置state的状态, 不方便调整。使用reduce将整个数据的状态进行集中管理设置

import React, {  Fragment,  useState,  useEffect,  useReducer,   // 引入usrReducer} from 'react';import axios from 'axios';const dataFetchReducer = (state, action) => { // 定义useReducer触发函数...};const useDataApi = (initialUrl, initialData) => {  const [url, setUrl] = useState(initialUrl);  const [state, dispatch] = useReducer(dataFetchReducer, { // 使用useReducer    isLoading: false,    isError: false,    data: initialData,  });...};复制代码

APP组件中的useEffect

useEffect(() => {  const fetchData = async () => {    dispatch({ type: 'FETCH_INIT' }); // 使用dispatch派发设置state    try {      const result = await axios(url);      dispatch({ type: 'FETCH_SUCCESS', payload: result.data });    } catch (error) {      dispatch({ type: 'FETCH_FAILURE' });    }  };  fetchData();}, [url]);复制代码

使用useDataApi进行封装之后

const useDataApi = (initialUrl, initialData) => {  const [url, setUrl] = useState(initialUrl);  const [state, dispatch] = useReducer(dataFetchReducer, {    isLoading: false,    isError: false,    data: initialData,  });...  return [state, setUrl];};复制代码

dataFetchReducer 设置返回的state

const dataFetchReducer = (state, action) => {  switch (action.type) {    case 'FETCH_INIT':      return {        ...state,        isLoading: true,        isError: false      };    case 'FETCH_SUCCESS':      return {        ...state,        isLoading: false,        isError: false,        data: action.payload,      };    case 'FETCH_FAILURE':      return {        ...state,        isLoading: false,        isError: true,      };    default:      throw new Error();  }};复制代码

设置didCance 当多个组件公用一个自定义的hook的时候 得防止未安装的组件对状态的设置

const useDataApi = (initialUrl, initialData) => {  const [url, setUrl] = useState(initialUrl);  const [state, dispatch] = useReducer(dataFetchReducer, {    isLoading: false,    isError: false,    data: initialData,  });  useEffect(() => {    let didCancel = false;    const fetchData = async () => {      dispatch({ type: 'FETCH_INIT' });      try {        const result = await axios(url);        if (!didCancel) {          dispatch({ type: 'FETCH_SUCCESS', payload: result.data });        }      } catch (error) {        if (!didCancel) {          dispatch({ type: 'FETCH_FAILURE' });        }      }    };    fetchData();    return () => {      didCancel = true;    };  }, [url]);  return [state, setUrl];};复制代码

这是去年八月那会的笔记 今天总结到掘金,自己返回来再看看基础的知识 这里面的hook-api都是大佬封装的 源码打开仔细去看我发现为啥人家就能年入百几万,我们就这么low。

最后对自己说一句:学习渠道和学习方式很重要。 如果有什么好的学习渠道和方式求大家评论下方推荐给我,我们相互学习一波。

作者:Liter
链接:https://juejin.im/post/5e4a5557e51d4526d326b0a3
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

你可能感兴趣的文章
JTAG - Debug Cable Driver/Receiver
查看>>
音乐播放器…
查看>>
【题解】Luogu P3123 [USACO15OPEN]贝茜说哞Bessie Goes Moo
查看>>
字符串课后作业
查看>>
LINQ 联查多表数据并封装到ViewModel的实现
查看>>
一些基本的C/C++数据类型
查看>>
设计模式之原型,学习笔记
查看>>
Spring顾问
查看>>
oracle导出数据到文本、从文本导入数据
查看>>
复利计算- 结对
查看>>
c# 实现文件批量压缩
查看>>
用Python的导入csv、文本文件、Excel文件的数据
查看>>
增加Myecllipse内存
查看>>
javascript中__proto__和prototype的区别
查看>>
SQL语句各子句的执行顺序
查看>>
牛客网-《剑指offer》-跳台阶
查看>>
其他常用类
查看>>
规划好职业,安排好未来的生活
查看>>
shell函数和参数传递
查看>>
Python3 pip出现Fatal error in launcher: Unable to create process using '"'
查看>>