Here in functional component useSelector is not working and not fetching any images.I have also attached my codesandbox link below.
https://codesandbox.io/s/how-to-use-redux-in-your-react-app-with-axios-forked-sgzunt?file=/src/component/users.js
import React, { useEffect } from "react";
import { useSelector } from "react-redux";
import { getUsers } from "../store/actions/usersAction";
function Users(props) {
const users = useSelector((state) => state.users);
useEffect(() => {
getUsers();
}, []);
if (!users.length) return null;
const userData = users.map((user) => {
return (
<React.Fragment key={user.id}>
<h6> {user.first_name} </h6>
<p> {user.last_name} </p>
<p> {user.email}</p>
<p>
<img key={user.avatar} src={user.avatar} alt="avatar" />
</p>
</React.Fragment>
);
});
return <div>{userData}</div>;
}
export default Users;
I got a bit confused with your code. You are using a function that returns a function that takes dispatch as an argument, likely as part of a redux-thunk action creator (i.e. async actions). https://redux.js.org/tutorials/fundamentals/part-6-async-logic
You are updating your state with the users for userReducer object so your selector function should be state.users.users
I just updated with classic Js and it is working. I am trying to achieve your functionality by using nested arrow functions.
This is the working code in Classic Js.
users is empty when your components mount thats why its not rendering anything, the action is called in next line.
const [data,setData] = useState([])
useEffect(()=>{ setData(users) },[users])
replace below code
if (!data.length) return null;
replace map function too.
const userData = data.map((user) => {
return (
<React.Fragment key={user.id}>
<h6> {user.first_name} </h6>
<p> {user.last_name} </p>
<p> {user.email}</p>
<p>
<img key={user.avatar} src={user.avatar} alt="avatar" />
</p>
</React.Fragment>
);
});
Related
I want to call api and generate div using data from api, but I don't know why this code is not working. It doesn't show anything on the page.
This is my code. countryArray is an object array, and it has property of population, name, continent, capital.
import React from 'react'
function Countries() {
fetch("https://restcountries.com/v3.1/all")
.then((response)=>response.json())
.then((countryArray)=>{
return (
<div>
{countryArray.map((country)=>(
<div className="Country_wrapper">
<div className="Flag_wrapper">
</div>
<div className="Explanation_wrapper">
<h2>{country.name}</h2>
<p>Population: {country.population}</p>
<p>Region: {country.continents}</p>
<p>Capital: {country.capital}</p>
</div>
</div>
))}
</div>
)
},
(err)=>{
console.log(err);
})
}
export default Countries
Hello there first of all you need save the api data in a state and then fetch the api in useEffect then you can use the api data in your react app
import React , {useState , useEffect} from 'react';
function app() {
const [examples , setExamples] = useState([]);
useEffect(() => {
fetch('https://restcountries.com/v3.1/all')
.then((res) => res.json())
.then((data) => {
setExamples(data);
})
.catch((err) => console.log(err));
},[]);
return(
<>
<div>
{
examples.map((example) => (
<div className="Country_wrapper">
<div className="Flag_wrapper">
</div>
<div className="Explanation_wrapper">
<h2>{example.name.official}</h2>
<p>Population: {example.population}</p>
<p>Region: {example.continents}</p>
<p>Capital: {example.capital}</p>
</div>
</div>
))
}
</div>
</>
);
}
export default app
this code is working
You need to return a jsx element. The usual way of doing data fetching inside react component is to do it inside an effect.
A minimal example would be like this.
function Countries() {
const [countryArray, setCountryArray] = useState([]);
useEffect(() => {
(async function () {
const res = await fetch("https://restcountries.com/v3.1/all");
const json = await res.json();
setCountryArray(json)
})()
}, [])
return (
<div>
{countryArray.map((country)=>(
<div className="Country_wrapper">
<div className="Flag_wrapper">
</div>
<div className="Explanation_wrapper">
<h2>{country.name.common}</h2>
<p>Population: {country.population}</p>
<p>Region: {country.continents}</p>
<p>Capital: {country.capital}</p>
</div>
</div>
))}
</div>
)
}
Ofc you should also take care of race conditions, errors, loading states, or use a library that does all this stuff for you and more like react query.
Check the documentation for more information, fetching data
You can't return jsx from fetch, that won't be rendered.
Use useState inside a useEffect to save the data, then return from the functinon itself
const {useState, useEffect} = React;
function Countries() {
const [ data, setData ] = useState([])
useEffect(() => {
function getData() {
fetch("https://restcountries.com/v3.1/all")
.then((response) => response.json())
.then((countryArray) => setData(countryArray)
);
};
getData();
}, [ ]);
return (
<div>
{data.map((country)=>(
<div className="Country_wrapper">
<div className="Flag_wrapper">
</div>
<div className="Explanation_wrapper">
<h2>{country.name.common}</h2>
<p>Population: {country.population}</p>
<p>Region: {country.continents}</p>
<p>Capital: {country.capital}</p>
</div>
</div>
))}
</div>
)
}
ReactDOM.render(<Countries />, document.getElementById("react"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Demo takes quite some time to load, so here's a pic:
Compiled with problems:X
ERROR
src\component\Products.jsx
Line 8:34: React Hook "useState" is called in function "getAllProducts" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use" react-hooks/rules-of-hooks
I'm trying to use useState, what is the best way to fetch the data from database to frontend
import React, { useState } from 'react';
import { NavLink } from 'react-router-dom';
import DATA from '../Data';
const getAllProducts = () => {
const [products, getproducts] = useState({
title : '',
price : '',
image : ''
});
const {title, price, image}= products;
let getproduct = fetch('http://localhost:6000/products/allProducts', {
method : 'GET',
headers : {
'Content-Type':'application/json'
},
body : JSON.stringify({
title, price, image
})
})
const cardItem = (item) => {
return(
<div className="card mx-3 my-5 py-3 px-2" key={item.id} style={{width: "15rem"}} id="cards">
<img src={item.image} className="card-img-top" alt={item.title} />
<div className="card-body text-center">
<h5 className="card-title fw-bolder" id="para">{item.title}</h5>
<p className="lead fw-bold" id="para">${item.price}</p>
<NavLink to={`products/${item.id}`} className="btn btn-outline-danger fw-bolder px-5 rounded-pill" id="para">Buy Now</NavLink>
</div>
</div>
);
}
return (
<div>
<div className="container py-2 mt-5 pt-5">
<div className="row">
<div className="col-12 text-center">
<h1 className="display-6 fw-bolder text-center" id="late">Latest Collections</h1>
<hr/>
</div>
</div>
</div>
<div className="container" id="products">
<div className="row justify-content-around">
{getproduct.map(cardItem)}
</div>
</div>
</div>
);
}
export default getAllProducts;
Hi
First I notify you of errors in your code and then expose you a solution:
In getAllProducts you use a useState but you can't use it this way, it's not a React component.
You also call getAllProducts.map but getAllProducts doesn't return any data array
In your useState the initial value represents ONLY one object of your data array
I would advise you this one , it's near of what you have done.
We create a method to get the data, a state to store the data and we display them conditionally
In my example below I use the axios library, it is an alternative to the fetch method that you use but the principle remains the same with the fetch method (except for one line of code)
I hope it helps you
import React , {useState,useEffect} from 'react'
import axios from 'axios'
const getAllProducts = async ()=>{
//Here is the fetch method
try{
const fetchQuery = await axios.get('my-url')
return fetchQuery.data
}
catch(error){
console.log(error)
}}
const CardItem = (props)=>(
//here you card component that will be mapped
<div>
<p>{props.data.title}</p>
<p>{props.data.price}</p>
</div>
)
const MappedCards = ()=>{
//Our initial state is a empty array
const [allProducts,setProducts] = useState([])
useEffect(()=>{
// I use the hook useEffect so when the MappedCards component is mounted
// I can fetch data and use the setProducts method
const initProducts = async ()=>{
const data = await getAllProducts()
setProducts(data)
}
//I call the method to initProducts
initProducts()
},[])
// I can even make something really cool here
if(allProducts.length === 0){
return <span>Loading data...</span>
}
// Now when allProducts will be filled with data I'll show this
return (<div className="my-container">
<h1>My shop</h1>
{allProducts.map(product => (<CardItem key={product.id} data={product} />}
</div>)
}
export MappedCards
I have a simple master-detail scenario where on the left side, I load a list of cities using useSwr with a REST service, then on the right side I have a city detail windows that also uses useSwr to load a single city (either from clicked on left, or first load).
In the code below, I'm calling the useEffect function, and then using the data retrieved from useSwr in a state setting call (setSelectedCityId).
This works, and there has always been data associated with the cities array, but I'm wondering if it is guaranteed that useEffect's function will run AFTER the Suspense promise is completed (like it seems to be).
Here is my simple code:
import { Suspense, useEffect, useState, useTransition } from "react";
import useSwr from "swr";
const fetcher = (...args) => fetch(...args).then((res) => res.json());
function CityDetailFallback() {
return <div>Loading (CityDetail)</div>;
}
function CityDetail({ selectedCityId }) {
function CityDetailUI({ selectedCityId }) {
const { data: city } = useSwr(
selectedCityId
? `http://localhost:3000/api/city/${selectedCityId}`
: null,
fetcher,
{
suspense: true,
}
);
if (!city) {
return <div>loading city...</div>
}
return (
<div className="row">
<div className="col-9">
<div>{JSON.stringify(city)} </div>
</div>
</div>
);
}
return (
<Suspense fallback={<CityDetailFallback />}>
<CityDetailUI selectedCityId={selectedCityId}></CityDetailUI>
</Suspense>
);
}
function CityList({
setSelectedCityId
}) {
//
const { data: cities } = useSwr("http://localhost:3000/api/city", fetcher, {
suspense: true,
});
useEffect(() => {
setSelectedCityId(cities[0].id);
}, []);
return (
<div className="col-3">
{cities.map((city) => {
return (
<div key={city.id}>
<button
onClick={(e) => {
setSelectedCityId(city.id);
}}
>
{city.city}
</button>
</div>
);
})}
</div>
);
}
export default function App() {
const [selectedCityId, setSelectedCityId] = useState();
return (
<div className="container">
Site Root
<hr />
<Suspense fallback={<div>Loading...</div>}>
<div className="row">
<div className="col-3">
<b>CITY LIST</b>
<hr />
<CityList
setSelectedCityId={setSelectedCityId}
selectedCityId={selectedCityId}
/>
</div>
<div className="col-9">
<div>
<b>CITY DETAIL (TOP ROW SELECTED AUTOMATICALLY)</b>
<hr />
<CityDetail selectedCityId={selectedCityId} />
</div>
</div>
</div>
</Suspense>
</div>
);
}
Note: I can't create a code sandbox because of a current bug in useSwr around suspense. https://github.com/vercel/swr/issues/1906 I'm testing currently with Create React App and using a dummy api endpoint for the REST calls. sorry :(
Yes, in React 18 useEffect always runs when the tree is consistent. So effects fire only after the tree is ready and not suspended.
I'm using firestore database to store my data in the collection "listings". So for each document in "listings", I need to render a <BookListing/> element in Home.js with the data from each document. From my research, there are a few other questions similar to this one out there, but they're outdated and use different react syntax. Here's my code:
function BookListing({id, ISBN, title, image, price}) {
return (
<div className="bookListing">
<div className='bookListing_info'>
<p className="bookListing_infoTitle">{title}</p>
<p className="bookListing_infoISBN"><span className="bookListing_infoISBNtag">ISBN: </span>{ISBN}</p>
<p className="bookListing_infoPrice">
<small>$</small>
{price}
</p>
</div>
<img className="bookListing_img" src={image} alt=""></img>
<button className="bookListing_addToCart">Add to Cart</button>
</div>
)
}
export default BookListing
function Home() {
document.title ="Home";
useEffect(() => {
getDocs(collection(db, 'listings'))
.then(queryCollection => {
queryCollection.forEach((doc) => {
console.log(doc.id, " => ", doc.data());
const element = <BookListing id="456" ISBN="0101" title="sample_title" image="https://nnpbeta.wustl.edu/img/bookCovers/genericBookCover.jpg" price="25"/>;
ReactDOM.render(
element,
document.getElementById('home-contents-main')
);
})
});
}, []);
return (
<div className="home">
<div className="home_container">
<div id="home-contents-main" className="home_contents">
</div>
</div>
</div>
)
}
export default Home
It's best (and most common) to separate the task into two: asynchronously fetching data (in your case from firestore), and mapping that data to React components which are to be displayed on the screen.
An example:
function Home() {
// A list of objects, each with `id` and `data` fields.
const [listings, setListings] = useState([]) // [] is the initial data.
// 1. Fetching the data
useEffect(() => {
getDocs(collection(db, 'listings'))
.then(queryCollection => {
const docs = [];
queryCollection.forEach((doc) => {
docs.push({
id: doc.id,
data: doc.data()
});
// Update the listings with the new data; this triggers a re-render
setListings(docs);
});
});
}, []);
// 2. Rendering the data
return (
<div className="home">
<div className="home_container">
<div className="home_contents">
{
listings.map(listing => (
<BookListing
id={listing.id}
ISBN={listing.data.ISBN}
title={listing.data.title}
image={listing.data.image}
price={listing.data.price}
/>
))
}
</div>
</div>
</div>
);
}
Some tips:
Fetching data from other web servers or services can be, and typically is, done in the same manner.
This example could be improved a lot in terms of elegance with modern JS syntax, I was trying to keep it simple.
In most cases, you don't want to use ReactDOM directly (only for the entry point of your app), or mess with the DOM manually; React handles this for you!
If you're not familiar with the useState hook, read Using the State Hook on React's documentation. It's important!
You can create a reusable component, and pass the data to it, and iterate over it using map() . define a state, and use it within the useEffect instead of creating elements and handling the process with the state as a data prop.
function BookListing({ id, ISBN, title, image, price }) {
return (
<div className="bookListing">
<div className="bookListing_info">
<p className="bookListing_infoTitle">{title}</p>
<p className="bookListing_infoISBN">
<span className="bookListing_infoISBNtag">ISBN: </span>
{ISBN}
</p>
<p className="bookListing_infoPrice">
<small>$</small>
{price}
</p>
</div>
<img className="bookListing_img" src={image} alt=""></img>
<button className="bookListing_addToCart">Add to Cart</button>
</div>
);
}
function Home() {
const [data, setData] = useState([]);
useEffect(() => {
document.title = 'College Reseller';
getDocs(collection(db, 'listings')).then((queryCollection) => setData(queryCollection));
}, []);
return (
<div className="home">
<div className="home_container">
<div id="home-contents-main" className="home_contents">
{data.map((doc) => (
<BookListing
id="456"
ISBN="0101"
title="sample_title"
image="https://nnpbeta.wustl.edu/img/bookCovers/genericBookCover.jpg"
price="25"
/>
))}
</div>
</div>
</div>
);
}
export default Home;
Again the same tutorial I have a new problem in the cart I am still new to using react redux so thanks for any help for explained I am creating the list of products ordered and counted (after) to be able to modify them added or canceled
TypeError: Cannot destructure property 'cartItems' of 'cart' as it is undefined.
import React, { useEffect } from 'react';
import { addToCart } from '../actions/cartActions';
import { useDispatch, useSelector } from 'react-redux';
function CartScreen(props) {
const cart = useSelector(state => state.cart);
const { cartItems } = cart;
const productId = props.match.params.id;
const qty = props.location.search ? Number(props.location.search.split("=")[1]) : 1;
const dispatch = useDispatch();
useEffect(() => {
if (productId) {
dispatch(addToCart(productId, qty));
}
}, []);
return <div className="cart">
<div className="cart-list">
<ul className="cart-list-container">
<li>
<h3>
Shopping Cart
</h3>
<div>
Price
</div>
</li>
{
cartItems.length === 0 ?
<div>
Cart is empty
</div>
:
cartItems.map(item =>
<li>
<div className="cart-image">
<img src={item.image} alt="product" />
</div>
<div className="cart-name">
<div>
Qty:
<select value={item.qty} onChange={(e) => dispatch(addToCart(item.product, e.target.value))}>
{[...Array(item.countInStock).keys()].map(x =>
<option key={x + 1} value={x + 1}>{x + 1}</option>
)}
</select>
</div>
</div>
<div className="cart-price">
${item.price}
</div>
</li>
)
}
</ul>
</div>
<div className="cart-action">
</div>
</div>
}
export default CartScreen;
A default value is needed. Somewhere at top-level modules of store:
const EMPTY_CART = { cartItems: [] }; // To ensure that default value is singleton and avoid useless re-renders
inside component:
const cart = useSelector(state => state.cart || EMPTY_CART);
const { cartItems } = cart;
It is better to define default values at reducers level.
Another approach is to pass defaultValue to createStore.
https://redux.js.org/recipes/structuring-reducers/initializing-state