0

I'm developing a React application that fetches posts (tweets) from an API using React Query, but I'm having trouble rendering the data after it's fetched.

Setup Overview:

  • I'm using React with React Query to manage data fetching.
  • The API successfully returns a list of tweets, which I can confirm by checking the console output.
  • Despite the data being fetched correctly, the posts state does not render in my component.

Code Snippet:

Here’s a simplified version of my component:

const Posts = ({ feedType, username, userId, currentUserId }) => {
const [posts, setPosts] = useState([]);
const navigate = useNavigate();

const getPostEndpoint = () => {
    // Logic to determine the endpoint based on feedType
};

const { isLoading, refetch, isRefetching, error } = useQuery({
    queryKey: ["posts", feedType, username, userId],
    queryFn: async () => {
        const token = localStorage.getItem('token');
        if (!token) {
            navigate('/');
            return [];
        }

        const response = await fetch(getPostEndpoint(), {
            headers: {
                'Authorization': `Bearer ${token}`,
                'Content-Type': 'application/json',
            }
        });

        if (!response.ok) throw new Error('Failed to fetch posts');
        const parsedData = await response.json();
        return parsedData.tweet_feed || [];
    },
    onSuccess: (data) => {
        setPosts(data);
    },
    enabled: !!username && !!localStorage.getItem('token'),
});

// Render logic
return (
    <>
        {(isLoading || isRefetching) && <PostSkeleton />}
        {!isLoading && !isRefetching && posts.length === 0 && <p>No posts in this tab.</p>}
        {!isLoading && !isRefetching && posts.length > 0 && (
            <div>
                {posts.map((post) => (
                    <Post key={post.id} post={post} currentUserId={currentUserId} />
                ))}
            </div>
        )}
        {error && <div>Error fetching posts: {error.message}</div>}
    </>
);

};

API Response:

I confirmed that the API is working as expected. Here's an example response from the /api/explore/ endpoint:

{
"tweets": [
    {
        "id": 2,
        "content": "amir",
        "creation_date": "2024-09-13"
    },
    {
        "id": 1,
        "content": "Hello!",
        "creation_date": "2024-09-13"
    }
]

}

Backend View (views.py): Here’s the relevant part of my Django backend that handles the request:

class GetTweetFeedView(viewsets.ViewSet):
permission_classes = [IsAuthenticated]

def list(self, request):
    profile = get_object_or_404(BasicUserProfile, user=request.user)
    followings = Follower.objects.filter(follower=profile)
    following_ids = [following.following.id for following in followings]
    tweets = Tweet.objects.filter(user_id__in=following_ids).order_by("-id")
    
    response_data = [
        {
            'id': tweet.id,
            'content': tweet.content,
            'creation_date': tweet.creation_date.strftime('%Y-%m-%d %H:%M:%S'),
            # Additional fields...
        }
        for tweet in tweets
    ]

    return Response({'tweet_feed': response_data}, status=200)

Console Output: In the console, I can see the fetched data logged correctly:

Fetched data: 
Object { tweets: (2) [...] }

Issues Encountered:

  1. Posts State Not Rendering: Despite seeing that the posts state is being set, the component does not render the posts.
  2. Empty Array: The posts state shows as an empty array when the component first renders, even though the API responds with data.

Questions:

  1. Why might the posts state not be rendering even though the data is fetched correctly?
  2. Is there anything in my use of React Query that could be causing this issue?
  3. Could there be an issue with how I'm accessing the data returned from the API?

Any help or insights would be greatly appreciated!

6
  • As a test... If you replace the API call with an operation (perhaps wrapping a Promise around a short timeout) which resolves to the known good data you're using, does the problem persist? If so, can it then be demonstrated with a minimal reproducible example? (Ideally a React code snippet but if that's prohibitive then even off-site would be better than nothing.) Commented Oct 4, 2024 at 13:35
  • 1
    It doesn't look like your curl response is a list, but you're using it like a list in the component (when it finishes loading the data). Are you sure you're not seeing any errors in the console? Commented Oct 4, 2024 at 15:22
  • @ChadS. this wouldn't cause any messages in console from what i can see, just silently go into the No posts-clause, just as described. i agree this is the issue. Commented Oct 18, 2024 at 8:08
  • @Anton Thanks! I updated the question. I wrote curl response like a list and it is not a list in reality. Commented Oct 28, 2024 at 3:47
  • @ChadS. Thanks! I updated the question. I wrote curl response like a list and it is not a list in reality. Commented Oct 28, 2024 at 3:50

2 Answers 2

0

I suggest to you that in the first step use from axios for requests.

I think you should remove onSuccess function for gets data and instead of onSuccess function, get data from queryFn function.

try this:

const {data: tweetsData, isLoading, refetch, isRefetching, error} = useQuery({
    queryKey: ["posts", feedType, username, userId],
    queryFn: async () => {
        const token = localStorage.getItem('token');
        if (!token) {
            navigate('/');
            return [];
        }

        const response = await fetch(getPostEndpoint(), {
            headers: {
                'Authorization': `Bearer ${token}`,
                'Content-Type': 'application/json',
            }
        });

        if (!response.ok) throw new Error('Failed to fetch posts');
        const parsedData = await response.json();

        const tweets = parsedData.tweet_feed.tweets || []; // get your posts from `tweets` object

        setPosts(tweets)

        console.log(tweets)

        return tweets
    },
    enabled: !!username && !!localStorage.getItem('token'),
    initialData: []
});

in the result you should have this

enter image description here

Sign up to request clarification or add additional context in comments.

1 Comment

I did the same as you said and had the same repsonse on the console but the problem is rendering and I could not get the data in my app again. I got fetching data json response error too.
0

The issue of React not rendering fetched posts from the API was due to the backend API not returning data in the format that the React front end expected. Specifically, the frontend expected an array of posts, but the response structure from the backend was not aligned with this expectation. The key change was to ensure that the backend always returns data in the form of an array, as expected by the React frontend.

Key Fixes:

  1. Ensure API Returns Data as Arrays: The backend API was updated to return an array of posts (or tweets) where each post is represented by an array containing multiple attributes. This ensures that the React frontend can process and render the posts correctly.

    In the corrected backend code, response_data is constructed as an array of tweet data, and each tweet is represented as an array with the necessary fields:

    response_data = [
        [
            tweet.id,
            tweet.content,
            tweet.image.url if tweet.image else None,
            tweet.video.url if tweet.video else None,
            tweet.creation_date.strftime('%Y-%m-%d %H:%M:%S'),
            [
                tweet.user.id,
                tweet.user.user.username,
                tweet.user.full_name,
                tweet.user.profile_photo.url if tweet.user.profile_photo else 'https://yookapp.ir/media/profile_photo/profile.jpg',
            ],
            "promoted" if tweet == selected_promoted_tweet else "regular",  # Mark the promoted tweet
            tweet.tweet_like_amount,  # Add the like count from the model field
            tweet.tweet_comment_amount,  # Add the comment count from the model field
            tweet.view_count  # Add the view count from the model field
        ]
        for tweet in combined_tweets
    ]
    

    This ensures that the data returned by the backend is an array, where each element is a list of tweet attributes that the frontend expects.

  2. Frontend Expectation of Arrays: In the frontend React code, the useQuery hook fetches posts and expects the backend to return the data as an array. If the response from the API is not in the expected format, React will not be able to render it properly. By ensuring that the backend returns an array of posts, React can then process and display the data accordingly.

  3. Frontend Code Example: The React component (Posts) utilizes useQuery to fetch posts. Since the backend now returns the data as an array, React can map over the postsWithPromoted array to render the posts:

    {postsWithPromoted.map((post) => (
        <Post
            key={post.id}
            post={{
                id: post.id,
                content: post.content,
                image: post.image,
                video: post.video,
                creationDate: post.creationDate,
                user: post.user,
                comments: post.comments,
                isPromoted: post.postType === "promoted", // Add a flag for promoted posts
            }}
        />
    ))}
    

Backend Changes Overview:

  • Fetch Tweets: Tweets are fetched based on the user's followings and excluding reported tweets.

  • Promoted Tweets: If there are promoted tweets, one is randomly selected and inserted into the feed at a random interval (e.g., every 4, 6, or 8 posts). This random placement mimics the behavior of mixed promoted content in a timeline.

  • Data Structure: The data returned is now in a structure that matches the frontend's expectation, with each tweet being an array of attributes.

Summary:

By adjusting the backend to always return an array of posts, React can now correctly render the posts from the API. This change was crucial because the React frontend expected data in array format, and without it, the posts could not be rendered. The backend is now properly serializing and sending the data as an array, ensuring seamless integration with the frontend.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.