简体   繁体   中英

Re-render page on clicking button even though dependency array object remains unchanged remains in Reactjs

I am using React 17.0.1. I am not able to figure out the following:

  1. Page re-renders if the button is clicked multiple times even when the id remains unchanged.
  2. Also I am encountering another error: Line 25:6: React Hook useEffect has a missing dependency: 'id'. Either include it or remove the dependency array react-hooks/exhaustive-deps. I am guessing this error is caused due to the fact id is set outside of the render.
import React, { useState, useEffect } from "react";
import axios from "axios";

function DataFetcing() {
  const [post, setPost] = useState({});
  const [id, setId] = useState(1);
  const [search, setSearch] = useState(1);

  const handleClick = () => {
    console.log(post);
    setSearch(post);
  };

  useEffect(() => {
    axios
      .get(`https://jsonplaceholder.typicode.com/posts/${id}`)
      .then((res) => {
        console.log(res);
        setPost(res.data);
      })
      .catch((err) => {
        console.log(err);
        setPost(err);
      });
  }, [search]);

  return (
    <div>
      <button type="button" onClick={handleClick}>
        Fetch Post
      </button>
      <input type="text" value={id} onChange={(e) => setId(e.target.value)} />
      <span key={post.id}>{post.title}</span>
    </div>
  );
}
export default DataFetcing;

I'm not sure what you are using the search state for. But if you just want the component to fetch when someone hits the Fetch Post button I would maybe add a searchId state. This solves all the complexities with the useEffect hook. I understand that you want to have a controlled input and therefore need the id state. In this case it is not terrible to add an additional state variable specifically for the id that you use to fetch.

 import { useEffect, useState } from "react"; import axios from "axios"; function DataFetcing() { const [post, setPost] = useState({}); const [id, setId] = useState(1); const [searchId, setSearchId] = useState(id) const [search, setSearch] = useState(1); const handleClick = () => { console.log(post); setSearch(post); setSearchId(id); }; useEffect(() => { axios.get(`https://jsonplaceholder.typicode.com/posts/${searchId}`).then((res) => { console.log(res); setPost(res.data); }).catch((err) => { console.log(err); setPost(err); }); }, [searchId]); return ( <div> <button type="button" onClick={handleClick}> Fetch Post </button> <input type="text" value={id} onChange={(e) => setId(e.target.value)} /> <span key={post.id}>{post.title}</span> </div> ); } export default DataFetcing;

  • 1) Your code fetches data every time button is clicked, because when you give [search] as a second argument to useEffect() you are asking React to execute useEffect() block every time search variable changes.

    You are using 'setSearch()' to set search value to an object, and even if the object's properties and corresponding values will be the same as before, the way javascript sees it, two objects will never be equal. You can test it and see that console.log({} === {}) outputs false .

    So, since you use 'setSearch()' in your handleClick() function to set search value to object, every time button is clicked it automatically triggers useEffect() and fetches data.

  • 2) React sees that you use id variable inside of useEffect() , so it assumes you want this effect to run if id is updated. It does not how you are updating id variable and it tries to protect you from accidentally using old fetched data if, for example, in a few months you decide to add another way to modify id variable (maybe 'fetch next post' button on the bottom of the page).

    This is part of useEffect() usefulness - it does not matter HOW id changes, data will be fetched, so you do not have to remember to do things like setSearch(Id) manually, which 1) is easy to forget and 2) has kind of vague intent when somebody reads code for the first time.

  • Solution: The most straightforward way I can think of to fix your code without more context is to change handleClick() function so it sets search to id (which will be a number) instead of post (which will be an object and always trigger useEffect() function, see 2) ...


    const handleClick = () => {
      console.log(post);
      setSearch(id);
    };

Your logic implies the following.

Initial values:

const [post, setPost] = useState({});
const [id, setId] = useState(1);
const [search, setSearch] = useState(1);

Values after first click (say: input is 2):

const [post, setPost] = useState({ /* post 2 response */ });
const [id, setId] = useState(2);
const [search, setSearch] = useState({});

Values after second click (input is again 2):

const [post, setPost] = useState({ /* post 2 response */ });
const [id, setId] = useState(2);
const [search, setSearch] = useState({ /* post 2 response */ });

As you can see, the state values are still changed. A simplest solution to this would be to just call your fetch from the handleClick function (and ommit the search state altogether).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM