3

i have a problem with hooks.

i'm using react-hooks, i have a button which at onClick getting data from api and setState with it.

Problem is:

when i click to button first time i get response from api but can't set it to state. When click to button second time i can setState. Why it happened ?

here is my component look like:

function App() {
  const [a, setA] = useState(null);


  const fetchData = () => {
    let data = {
        id: 1
    }


    axios.post(baseUrl, data)
    .then(res => {
      if (res.data) {
        setA(res.data)
      }
      console.log(a)
    })

  }

  return (
    <div className="App">
      <div>
        <button onClick={fetchData}></button>
      </div>
    </div>
  );
}

export default App;

i tried to using fetchData function like that:

 function fetchData() {
        let data = {
            id: 1
        }


        axios.post(baseUrl, data)
        .then(res => {
          if (res.data) {
            setA(res.data)
          }
          console.log(a)
        })

      }

but it's not helped too

user3348410
  • 2,733
  • 10
  • 51
  • 85

4 Answers4

4

a is a const. It's impossible to change it, so there's no way your console.log statement at the end of fetchData could ever log out something different than the value that a had when fetchData was created.

So that's not what setA does. The point of setA isn't to change the value of the const, but to cause the component to rerender. On that new render, a new set of variables will be created, and the new a will get the new value. Move your console.log out to the body of your component, and you'll see it rerender with the new value.

In short: Your code appears to be already working, you just have the wrong logging.

Nicholas Tower
  • 72,740
  • 7
  • 86
  • 98
0

If your scope is to fetch data, use this:

const [data, setData] = useState("");
  useEffect(async () => {
    const result = await axios(
      'here will be your api',
    );
    setData(result.data);
  });

Now your response will be stored in data variable.

Asking
  • 3,487
  • 11
  • 51
  • 106
0

I would not use an effect for it, effects are useful if the props or state changes and can thereby substitute lifecycle methods like componentDidMount, componentDidUpdate, componentWillUnmount, etc.. But in your case these props haven't changed yet (you want to change your state though). Btw, be aware that @Asking's approach will fetch data on EVERY rerender (props or state change). If you want to use useEffect, be sure to add the second parameter to tell React when to update.

Normally, your code should work, I haven't tested but looks legit. Have you used developer tools for react to check if the state/hook has changed? Because if you say it did not work because of the console.log printing: Have in mind that setA() is an async function. The state was most likely not yet changed when you try to print it. If you haven't react developer tools (which I highly recommend) you can check the real state by adding this code in the function:

useEffect(() => console.log(a), [a]);

I have a few real improvements to your code though:

    function App() {
  const [a, setA] = useState(null);


  const fetchData = useCallback(async () => {
    let data = {
        id: 1
    }


    const res = await axios.post(baseUrl, data);
    setA(res.data);

  }, []);

  return (
    <div className="App">
      <div>
        <button onClick={fetchData}></button>
      </div>
    </div>
  );
}

export default App;

By adding useCallback you ensure that the function is memorized and not declared again and again on every rerender.

Mika
  • 534
  • 5
  • 14
  • Sorry but it's not changed anything :/ – user3348410 Dec 15 '19 at 13:31
  • 1
    ups. sorry, it's worked !!! thank you so much that time i need to use useCallBack for every api request right? – user3348410 Dec 15 '19 at 13:33
  • You don't need useCallback actually, it is just a performance improvement & best practice. When using useCallback, it is needed to declare dependent parameters, so the function gets re-declared if they change. See [docs](https://reactjs.org/docs/hooks-reference.html#usecallback) – Mika Dec 15 '19 at 13:41
0

@Nicholas Tower has already answered your question. But if you are looking for code

function App() {
  const [a, setA] = useState(null);


  const fetchData = () => {
    let data = {
        id: 1
    }


    axios.post(baseUrl, data)
    .then(res => {
      if (res.data) {
        setA(res.data)
      }

    })

  }


  console.log(a)
  return (
    <div className="App">
      <div>
        <button onClick={fetchData}></button>
      </div>
    </div>
  );
}

export default App;

just place log before return (. This should do

krishnan
  • 782
  • 1
  • 9
  • 20
  • Actually i understood something that my entrie code worked well to but when i make conditions in return so like {a.length ? (

    {a}

    ) : null} . it's not working.
    – user3348410 Dec 15 '19 at 13:47
  • before getting the length of a. You should check if a is null like this { a && (

    {a}

    ) }. if you use your code {a.length ? (

    {a}

    ) : null} you will get error because is a is null for the first time and you are trying to get the length of null which will throw error
    – krishnan Dec 15 '19 at 14:01
  • I saw it and first state i set like const [a, setA] = useState([]); but this case not worked too. here eg. i have length it's 0 – user3348410 Dec 15 '19 at 14:04
  • sorry my mistake i maked it like const [a, setA] = useState(null); and my condition is { a != null (

    {a}

    ) }. then it's worked well
    – user3348410 Dec 15 '19 at 14:06
  • great. Reason why [a, setA] = useState([]) didn't worked is a.length resolve to 0 and 0 is actually false inside conditions – krishnan Dec 15 '19 at 14:08