Need help displaying favourites in React/Express app [duplicate]

In class based Component:

componentDidMount() {
    axios.get('https://jsonplaceholder.typicode.com/posts').then((res) => {
        this.setState({
            posts: res.data.slice(0, 10)
        });
        console.log(posts);
    })
}

I tried this:

const [posts, setPosts] = useState([]);

useEffect(() => {
    axios.get('https://jsonplaceholder.typicode.com/posts').then((res) => {
        setPosts(res.data.slice(0, 10));
        console.log(posts);
    })
});

It creates an infinite loop. If I pass a []/{} as the second argument[1][2], then it blocks further call. But it also prevents the array from updating.

[1] Infinite loop in useEffect

[2] How to call loading function with React useEffect only once


Solution 1:

Giving an empty array as second argument to useEffect to indicate that you only want the effect to run once after the initial render is the way to go. The reason why console.log(posts); is showing you an empty array is because the posts variable is still referring to the initial array, and setPosts is also asynchronous, but it will still work as you want if used in the rendering.

Example

const { useState, useEffect } = React;

function App() {
  const [posts, setPosts] = useState([]);

  useEffect(() => {
    setTimeout(() => {
      setPosts([{ id: 0, content: "foo" }, { id: 1, content: "bar" }]);
      console.log(posts);
    }, 1000);
  }, []);

  return (
    <div>{posts.map(post => <div key={post.id}>{post.content}</div>)}</div>
  );
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://unpkg.com/[email protected]/umd/react.production.min.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.production.min.js"></script>

<div id="root"></div>

Solution 2:

You can check how axios-hooks is implemented.

It's super simple and uses the config object (or url) you provide to decide when to make a request, and when not to, as explained in Tholle's answer.

In addition to allowing you to use the awesome axios in your stateless functional components, it also supports server side rendering, which - it turns out - hooks make very straightforward to implement.

Disclaimer: I'm the author of that package.

Solution 3:

I've written a Custom Hooks for Axios.js.

Here's an example:

import React, { useState } from 'react';

import useAxios from '@use-hooks/axios';

export default function App() {
  const [gender, setGender] = useState('');
  const {
    response,
    loading,
    error,
    query,
  } = useAxios({
    url: `https://randomuser.me/api/${gender === 'unknow' ? 'unknow' : ''}`,
    method: 'GET',
    options: {
      params: { gender },
    },
    trigger: gender,
    filter: () => !!gender,
  });

  const { data } = response || {};

  const options = [
    { gender: 'female', title: 'Female' },
    { gender: 'male', title: 'Male' },
    { gender: 'unknow', title: 'Unknow' },
  ];

  if (loading) return 'loading...';
  return (
    <div>
      <h2>DEMO of <span style={{ color: '#F44336' }}>@use-hooks/axios</span></h2>
      {options.map(item => (
        <div key={item.gender}>
          <input
            type="radio"
            id={item.gender}
            value={item.gender}
            checked={gender === item.gender}
            onChange={e => setGender(e.target.value)}
          />
          {item.title}
        </div>
      ))}
      <button type="button" onClick={query}>Refresh</button>
      <div>
        {error ? error.message || 'error' : (
          <textarea cols="100" rows="30" defaultValue={JSON.stringify(data || {}, '', 2)} />
        )}
      </div>
    </div>
  );
}

You can see the result online.