是否可以将 useEffect 放在 if 语句中?

IT技术 javascript reactjs use-effect
2021-05-15 17:59:33

伙计们,我一直在处理我的登陆和仪表板页面。

所以页面的工作流程是这样的:用户进入登陆页面,在那里他可以选择插入表单位置,或者按下一个按钮来接收所有位置。现在,在后台我做了两个API一个获得所有位置,和第二,我已经添加了:location作为req.body.param基于该PARAM,然后过滤器的位置。在邮递员中一切正常。

现在,因为我有两种方式让用户获取位置(所有这些或他想要的一些)我认为我将两个useEffects 放在 if 语句中,如下所示:

const filter = props.location.data;
if (filter) {
    useEffect(() => {
      const fetchFiltered = async () => {
        const res = await ArticleService.filterByName(filter);
        setContent(res.data);
      };

      fetchFiltered();
    }, []);
  } else {
    useEffect(() => {
      const fetchPosts = async () => {
        const res = await ArticleService.articles();
        setContent(res.data);
      };

      fetchPosts();
    }, []);
  }

所以我背后的逻辑是,如果有filter内部props.location执行我useEffectArticleService那里获取数据,然后在 api url 内发送过滤器。如果没有filter就检索我的所有数据,并将内容设置为res.data.

但是当我编译代码时,错误是这样的: React Hook "useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render

有没有办法用我的逻辑来做到这一点,或者我需要创建两个组件:一个普通仪表板,第二个用于过滤结果?

用户发送位置的 Landing.js

<Form>
   <div className='form-group'>
      <Input
       type='text'
       className='form-control text-center'
       name='name'
       placeholder='Enter desired location'
       value={location}
       onChange={onChangeLocation}
       />
      <Link to={{ pathname: '/dashboard', data: location }}>
         <i className='fas fa-check'></i>
      </Link>
   </div>

    <p className='text-center'>or</p>
    <Link className='btn btn-primary btn-block' to='/dashboard'>
       Show all locations
    </Link> 
</Form>

仪表板.js

import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';

import Pagination from 'react-js-pagination';

import ArticleService from '../../services/article.service';

const Dashboard = (props) => {
  const [content, setContent] = useState([]);
  const [currentPage, setCurrentPage] = useState(1);
  const [postsPerPage] = useState(10);

  const filter = props.location.data;

  if (filter) {
    useEffect(() => {
      const fetchFiltered = async () => {
        const res = await ArticleService.filterByName(filter);
        setContent(res.data);
      };

      fetchFiltered();
    }, []);
  } else {
    useEffect(() => {
      const fetchPosts = async () => {
        const res = await ArticleService.articles();
        setContent(res.data);
      };

      fetchPosts();
    }, []);
  }

  let counter = content.length;

  // Get current posts
  const indexOfLastPost = currentPage * postsPerPage;
  const indexOfFirstPost = indexOfLastPost - postsPerPage;
  const currentPosts = content.slice(indexOfFirstPost, indexOfLastPost);

  // Change page
  const handlePageChange = (pageNumber) => {
    setCurrentPage(pageNumber);
  };

  const render = (item, index) => {
    return (
      <tr key={index}>
        <td className='text-center'>
          <div key={item.id}>
            <img
              src={`${item.pictures}`}
              alt='slika artikla'
              className='rounded'
            ></img>
          </div>
        </td>
        <td className='text-center'>
          <div key={item.id}>
            <h4>{item.descr}</h4>
            <br></br>
            <h6 className='text-left'>Number of m2: {item.sqm}m2</h6>
            <div className='text-left'>
              <small className='text-left'>
                {' '}
                <a href={item.link} target='_blank' rel='noopener noreferrer'>
                  Show on website
                </a>
              </small>
            </div>
          </div>
        </td>
        <td className='text-center'>
          <div key={item.id}>
            <h4>{item.price}</h4>
            <small className='text-left'>Price per m2: {item.ppm2}</small>
          </div>
        </td>
        <td className='text-center'>
          <div key={item.id}>
            <Link to={`/article/${item.id}`}>
              <h4>Show</h4>
            </Link>
          </div>
        </td>
      </tr>
    );
  };

  return (
    <div>
      <div className='container'>
        <h4 className='text-center'>
          Number {counter}
        </h4>
        <div className='table-responsive'>
          <table className='table'>
            <thead className='thead-dark'>
              <tr>
                <th className='text-center' scope='col'>
                  Pic
                </th>
                <th className='text-center' scope='col'>
                  Description
                </th>
                <th className='text-center w-25' scope='col'>
                  Price
                </th>
                <th className='text-center' scope='col'>
                  Show offer
                </th>
              </tr>
            </thead>
            <tbody>{currentPosts.map(render)}</tbody>
          </table>
        </div>
      </div>
      <nav>
        <div className='w3-bar w3-xlarge'>
          <ul className='pagination justify-content-center'>
            <li className='page-item'>
              <Pagination
                hideDisabled
                hideNavigation
                hideFirstLastPages
                currentPage={currentPage}
                itemsCountPerPage={10}
                totalItemsCount={content.length}
                pageRangeDisplayed={indexOfLastPost}
                onChange={handlePageChange}
              />
            </li>
          </ul>
        </div>
      </nav>
    </div>
  );
};

export default Dashboard;

谢谢!:D

2个回答

基本答案,不,您不能有条件地调用 useEffect。您必须将条件逻辑放在 useEffect 回调中。

const filter = props.location.data
useEffect(() => {
  if (filter) {
    const fetchFiltered = async () => {
      const res = await ArticleService.filterByName(filter)
      setContent(res.data)
    }

    fetchFiltered()
  } else {
    const fetchPosts = async () => {
      const res = await ArticleService.articles()
      setContent(res.data)
    }

    fetchPosts()
  }
}, [filter, setContent, ArticleService.filterByName, ArticleService.articles])

React 中的 Hook 并没有真正遵循 javascript 的标准规则。它们的实现方式有一些性能原因,通常会进行一些缓存以阻止每次渲染通道完成时执行过多的代码。

useEffect 钩子只会在依赖数组中的一个值(useEffect 的第二个参数)发生变化的渲染期间运行它的回调函数。加入所有可能改变的外部值是标准的。因此,当过滤器的值发生变化时,应用程序将重新渲染,useEffect 将进行比较,意识到某些事情发生了变化并再次运行它的回调,然后执行 if 语句。

您可以在文档性能部分阅读更多相关信息

除了上面的答案,来自官方文档

不要在循环、条件或嵌套函数中调用 Hook。相反,始终在 React 函数的顶层使用 Hook。通过遵循此规则,您可以确保每次渲染组件时都以相同的顺序调用 Hook。这就是允许 React 在多个 useState 和 useEffect 调用之间正确保留 Hooks 状态的原因。