React Hooks 实现一个搜索功能

6,367 阅读3分钟

react 实现一个搜索功能

  • 要求实时搜索,得到结果,结果由接口数据返回
  • 实现图

image

来先用基本的 react 实现一个吧

import stores from './stores' // 事先准备好的接口库
import _ from 'loadsh'; // 使用 debounce,实时得到接口需要 debounce 

class Search extend Component{
    featchList = async (val = '')=>{
        try{
            const data = await stores.featchList(val);
            
            data && this.setState({
                data
            })
        }catch(err){
            throw(err)
        }
    }

    handleOnSearch = (ev)=>{
        console.log(ev);
        
        this.featchList(ev.target.value)
    }
    
    componentDidMount(){
        this.featchList();
    }
    
    render(){
        <div className="search-container">
            <input onChange={_.debounce(ev => this.handleOnSearch(ev), 300)}/>
            <div className="list">
                {this.state.data}
            </div>
        </div>
    }
}

如上一个简单的实时输入搜索,然后300ms延时展示结果的react就完成了,我们怎么使用 hooks 改装一下啦?

小知识 useEffect

  • useEffect(cakkBackFunc, array)
cakkBackFunc 可以返回一个函数,用作清理

array(可选参数):数组,用于控制useEffect的执行
* 分三种情况
    《1》空数组,则只会执行一次(即初次渲染render),相当于componentDidMount
    《2》非空数组,useEffect会在数组发生变化后执行
    《3》不填array这个数组,useEffect每次渲染都会执行

hooks 的现实搜索功能

function App() {
  const [data, setData] = useState([]);
  const [query, setQuery] = useState('');

  useEffect(() => {
    const featchList = async (query = '')=>{
        try{
            const data = await stores.featchList(query);
            
            data && setData(data);
        }catch(err){
            throw err;
        }
    }

    featchList(query); // 我们把 query 当做参数传进去,把data和query 联动起来这样就可以达到搜索的功能啦。
  }, []);

  return (
    <div className="search-container">
        <input onChange={_.debounce(ev => setQuery(ev.target.value), 300)}/>
        <div className="list">
            {this.state.data}
        </div>
    </div>
  );
}

export default App;
  • 我们可能会遇到相关问题: react 会报错下面插件的相关 warning

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

我们提供了一个 `exhaustive-deps ESLint` 规则作为 `eslint-plugin-react-hooks`
包的一部分。它会帮助你找出无法一致地处理更新的组件。

试试上面的代码,发现现在只实现了 componentDidMount 中一次 mount的数据获取,我们在输入 input 框的时候并没有去请求新的数据,这个时候我们就需要在 useEffect(a,b) 的第二个参数中放入 [query],当他发生变化的时候再重新触发 useEffect()

function App() {
  const [data, setData] = useState([]);
  const [query, setQuery] = useState('');

  useEffect(() => {
    const featchList = async (query = '')=>{
        try{
            const data = await stores.featchList(query);
            
            data && setData(data);
        }catch(err){
            throw err;
        }
    }

    featchList(query); // 我们把 query 当做参数传进去,把data和query 联动起来这样就可以达到搜索的功能啦。
  }, [query]); // 随着 query 的变化,重新 mount,获取新的搜索的数据

  return (
    ....
  );
}

export default App;

一个衍生问题?-- 在依赖列表中省略函数是否安全?

  • 这里可能有同学会问了,我们为什么要把 featchList 定义到 useEffect() 内部?直接和以前的写法一样放在外面有什么区别吗?

  • 我们来看这句话 ==如果你指定了一个 依赖列表 作为 useEffect、useMemo、useCallback 或 useImperativeHandle 的最后一个参数,它必须包含参与那次 React 数据流的所有值。这就包含了 props、state,以及任何由它们衍生而来的东西。==

function ProductPage({ productId }) {
  const [product, setProduct] = useState(null);

  async function fetchProduct() {
    const response = await fetch('http://myapi/product' + productId); // 使用了 productId prop
    const json = await response.json();
    setProduct(json);
  }

  useEffect(() => {
    fetchProduct();
  }, []); // 🔴 这样是无效的,因为 `fetchProduct` 使用了 `productId`
  // ...
}
  • 解决方案: 推荐的修复方案是把那个函数移动到你的 effect 内部。这样就能很容易的看出来你的 effect 使用了哪些 props 和 state
function ProductPage({ productId }) {
  const [product, setProduct] = useState(null);

  useEffect(() => {
    // 把这个函数移动到 effect 内部后,我们可以清楚地看到它用到的值。
    async function fetchProduct() {
      const response = await fetch('http://myapi/product' + productId);
      const json = await response.json();
      setProduct(json);
    }

    fetchProduct();
  }, [productId]); // ✅ 有效,因为我们的 effect 只用到了 productId
  // ...
}

总结,如果使用了props,state,或者他们的衍生值,我们需要把相关使用的函数放进 useEffect 中,然后把相关值放进 useEffect 的第二个参数中

参考