Can't get map to render on react page - javascript

im trying to render a list of objects, when i try map on console it returns the information correctly, i just need to render the object name in a list, but everytime i reload nothing renders on the screen, i don't know what i'm doing wrong.
import React from "react";
import { useState } from 'react'
import Swal from 'sweetalert2'
import AuthService from "../services/auth.service";
import { FcInfo } from 'react-icons/fc'
import { AiFillEdit } from 'react-icons/ai'
import { FiDelete } from 'react-icons/fi'
import { Card, InputGroup, FormControl, Button, DropdownButton, Dropdown,Table} from 'react-bootstrap';
import 'bootstrap/dist/css/bootstrap.min.css';
import '../auth/login.css'
import solicitudeService from "../services/solicitude.service";
import edificeService from "../services/edifice.service";
import { render } from "#testing-library/react";
function MySolicitudes(){
var data=[]
solicitudeService.getAll().then(response =>{
data=response.data
data.map((val, key)=>{
console.log("TEST:",val.nombresolicitud,key);
});
console.log(data);
}).catch(e=>{
console.log(e);
});
return(
<div className="container">
<h3>Lista Espacios</h3>
<ul className="list-group">
{data && data.map((val,index) =>{
return( <li key={index}>
{val.nombresolicitud}
</li>);
})
}
</ul>
</div>
);
} export default MySolicitudes;

The problem is that data isn't part of the component's state, so reassigning it won't trigger a re-render.
Also, your code will currently call solicitudeService.getAll() on every render, which you probably don't want.
Try using the useEffect and useState hooks:
const [data, setData] = useState([])
useEffect(() => {
solicitudeService.getAll().then(response =>{
setData(response.data);
console.log(data);
}).catch(e=>{
console.log(e);
});
}, []);
This will call solicitudeService.getAll() once when the component is initially rendered, and then call setData which should trigger a re-render and give you the behavior you expect.
You can adjust the deps array for useEffect if you want to change when solicitudeService.getAll() is called.

data is not a state variable here and as such does not excecute a rerender of your view (your load of the data via the solicitudeService is not synchronos and as such the data is not there when the page renders).
Put the data in a useState hook and call the setter inside of youre solicitudeService.
Something like:
const [data, setData] = useState()
solicitudeService.getAll().then(response =>{
setData(response.data)
}).catch(e=>{
console.log(e);
});

Related

Console log return an infinite value in react js

I want to display some datas from API using functional component instead of class component in react. In order to do so, I write useEffect and apparently work properly. The problem is, if I write console log, it would return an infinite value.
Any one can help me to solve this? I want the console log stop looping the value from my API. This is my source code. Thank you.
import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import axios from "axios";
export default function FoodDetail() {
const { id } = useParams();
const [detail, setDetail] = useState([]);
useEffect(() => {
axios
.get("http://localhost:3004/foods/" + id)
.then((res) => {
setDetail(res.data);
console.log(detail)
})
.catch((error) => {
console.log(error);
});
});
return ()
}
if you want the get to only run once on component mount, you can use the code below:
import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import axios from "axios";
export default function FoodDetail() {
const { id } = useParams();
const [detail, setDetail] = useState([]);
useEffect(() => {
axios
.get("http://localhost:3004/foods/" + id)
.then((res) => {
setDetail(res.data);
console.log(detail)
})
.catch((error) => {
console.log(error);
});
}, []);
return ()
}
The only difference is the inclusion of an empty dependency array as the second argument for useEffect(). Going forward, if you want to refetch this data based on the state of another variable changing, you can add that variable to the dependency array and this will refetch the data each time the value of that variable changes.
** edit **
To see the value of detail properly, you can remove the console.log from the first useEffect loop and add something like this to the code:
useEffect(()=> {
console.log(detail)
}, [detail])
your useeffect is running infinitely since you console log your call stack gets
full and you notice it so use following code so it will exactly run once like
componentdidmount
useEffect(() => {
axios
.get("http://localhost:3004/foods/" + id)
.then((res) => {
setDetail(res.data);
console.log(detail)
})
.catch((error) => {
console.log(error);
});
},[]); //add empty array as another parameter of useEffect so it run only
//once
Try adding an empty array to the UseEffect refreshing dependency.
UseEffect(() => {}, []) this empty array means UseEffect will only be triggered at component mounting, i guess yours would be called everytime component is re-rendered.
Example : UseEffect(() => {console.count('refresehd'), [detail]}) would be triggered everytime your detail changes
for more info check the UseEffect Documentation
Docs useEffect

Function call in non relation component in react js

ProductTable.js
import React, { useEffect, useState } from "react";
function ProductTable() {
useEffect(() => {
fetchData();
}
const fetchData = () => {
axios
.get("http://localhost:4000/api/products/product/viewAllProduct")
.then((res) => {
const getData = res.data.data;
console.log(getData);
setData(getData);
});
};
return ( jsx..)
}
export default ProductTable;
**ProductModals.js
**
``import React, { useEffect, useState } from "react";
function ProductModals(){
const handleSubmit=()=>{
.....
}
return (jsx..)
export default productModals;`
viewProduct.js
import React, { useEffect, useState } from "react";
import ProductTable from "../../Components/Tables/Product/ProductTable";
import ProductModals from "../../Components/Modals/Product/ProductModals";
function viewProduct(){
return(
<productModals/>
<productTable/>
)
}
export default viewProduct;
I need to to get fetchDatafunction from productTable.js component to productModal.js component. both components parent component is viewProduct.js. I tried many ways. but could not work. In productModal.js component has a function for form submit , when form submit done I need to call fetchData function, If anyone know the way please help me
you can do this through react hooks. First, use fetchData into the parent component to pass data into both child components, If needed. Also Once submit the form call fetchData function through it and Update through setData props. So its parent state is also updated.
viewProduct.js
import React, { useEffect, useState } from "react";
import ProductTable from "../../Components/Tables/Product/ProductTable";
import ProductModals from "../../Components/Modals/Product/ProductModals";
useEffect(() => {
fetchData();
}
const fetchData = () => {
axios
.get("http://localhost:4000/api/products/product/viewAllProduct")
.then((res) => {
const getData = res.data.data;
console.log(getData);
setData(getData);
});
};
return ( jsx..)
}
function viewProduct(){
return(
<productModals data={data} setData={setData}/>
<productTable data={data}/>
)
}
export default viewProduct;
productModel.js
Now, Submit your form and call the function recall inside and after getting response update through setData props.
const fetchData = () => {
axios
.get("http://localhost:4000/api/products/product/viewAllProduct")
.then((res) => {
const getData = res.data.data;
console.log(getData);
setData(getData);
});
};
const recall=useCallback(()=>fetchData(),[])
If the scenario is as simple as the example you have provided you can get away with lifting state up, but if you have many nested child components that needs to read/set these states you can use context, as suggested by Rajesh.
Lifting state up
To "lift state up" create a state in the parent component ViewProduct and pass along the relevant variables to the respective components via props:
function ViewProduct(){
const [done, setDone] = useState(false);
return(
<productModals setDone={setDone} />
<productTable done={done} />
)
}
Use the passed in prop setDone in your handleSubmit function to update the state:
function ProductModals({setDone}){
const handleSubmit=()=>{
.....
setDone(true);
}
And in ProductTable make useEffect dependant on done to only fetch when it is true:
function ProductTable({done}) {
useEffect(() => {
if (!done) return;
fetchData();
}, [done]);
const fetchData = () => {
axios
.get(...
Reuse state
If you need to reuse the done state, you can reset it by passing setDone to ProductTable and set it back to false when fetching data:
function ProductTable({done, setDone}) {
useEffect(() => {
if (!done) return;
fetchData();
setDone(false);
}, [done]);
const fetchData = () => {
axios
.get(...

How to pass state/data from one component to another in React.js (riot api specifically)

I am trying to pull information from one component's API call to then use that data in another API call in a separate component. However, I am unsure how to export and use the data from the first API call in the second component.
App.js
import './App.css';
import FetchMatch from './fetch-match/fetch.match';
import FetchPlayer from './fetch-player/fetch.player';
function App() {
return (
<div className="App">
<h1>Hello world</h1>
<FetchPlayer></FetchPlayer>
<FetchMatch></FetchMatch>
</div>
);
}
export default App;
fetch.player then makes the first API call to get a users specific ID which will be used in the second API call too fetch that users match history.
fetch.player.js
import React, { useEffect, useState } from 'react';
import axios from 'axios';
const FetchPlayer = () => {
const [playerData, setPlayerData] = useState([]);
const userName = 'users name';
const userTagLine = '1234';
const apiKey = '???';
useEffect( () => {
axios.get(`https://americas.api.riotgames.com/riot/account/v1/accounts/by-riot-id/${userName}/${userTagLine}?api_key=${apiKey}`)
.then(response => {
console.log(response.data)
setPlayerData([response.data])
})
.catch(error => console.log(error))
}, []);
return (
<div>
{playerData.map( data => (
<div>
<p>{data.puuid}</p>
<p>{data.gameName}#{data.tagLine}</p>
</div>
))}
</div>
)
}
export default FetchPlayer;
not much here but just in case...
fetch.match.js
import React, { useState } from 'react';
// Somehow take in the puuid set in the state of fetch.player to make a second API call below
const FetchMatch = () => {
const [matchData, setMatchData] = useState([]);
return (
<div>
// players match list goes here
</div>
)
}
export default FetchMatch;
I am unsure if I should make a separate function instead which would allow me to create consts to handle both API calls in a single file. Or if there is a way to pass the state from fetch.player as a prop to fetch.match from App.js. I have tried to do the former but it either doesn't work or I am messing up the syntax (most likely this)
If you render both component parallelly in a parent component, they are called sibling components.
Data sharing in sibling components can be done by multiple ways (Redux, Context etc) but the easiest and simplest way (the most basic way without 3rd party API) involves the use of parent as a middle component.
First you create the state in the parent component and provide it as props to the child component which need the data from its sibling (in your case is FetchMatch).
import React from 'react';
import './App.css';
import FetchMatch from './fetch-match/fetch.match';
import FetchPlayer from './fetch-player/fetch.player';
function App() {
const [data,setData] = React.useState();
return (
<div className="App">
<h1>Hello world</h1>
<FetchPlayer></FetchPlayer>
<FetchMatch data={data} ></FetchMatch>
</div>
);
}
export default App;
Provide the function to setData as a props to the child component which will fetch the initial API (in your case is FetchPlayer)
<FetchPlayer onPlayerLoad={(data) => setData(data)} />
Then, in that child component when you finish calling the API and get the result, pass that result to the onPlayerLoad function which will call the setData function with the result as parameters. It will lead to state change and re-rendering of the second FetchMatch component feeding the props data with API results.
import React, { useEffect, useState } from 'react';
import axios from 'axios';
const FetchPlayer = ({onPlayerLoad}) => {
const [playerData, setPlayerData] = useState([]);
const userName = 'users name';
const userTagLine = '1234';
const apiKey = '???';
useEffect( () => {
axios.get(`https://americas.api.riotgames.com/riot/account/v1/accounts/by-riot-id/${userName}/${userTagLine}?api_key=${apiKey}`)
.then(response => {
console.log(response.data)
setPlayerData([response.data])
onPlayerLoad(response.data)
})
.catch(error => console.log(error))
}, []);
return <></>;
Coming to FetchMatch, you will have the data in its second rendering.
import React, { useState } from 'react';
// Somehow take in the puuid set in the state of fetch.player to make a second API call below
const FetchMatch = ({data}) => {
const [matchData, setMatchData] = useState([]);
//console.log(data);
return (
<div>
// players match list goes here
</div>
)
}
export default FetchMatch;
Now, you can do whatever you want with the shared data in second component which in your case is trigger match API. 🎉

How Do you display data from an api in React?

Weather.JS File
import { useEffect, useState } from "react"
import axios from 'axios'
import WeatherDisplay from './WeatherDisplay'
const Weather = ({capital, params}) => {
const [weather,setWeather] = useState([])
useEffect(async () => {
const result = await axios.get('http://api.weatherstack.com/current', {params})
console.log(result.data)
setWeather(result.data)
},
[params])
return(
<div>
<h2>Weather in {capital}</h2>
<WeatherDisplay current={weather.current}/>
</div>
)
}
export default Weather
WeatherDisplay.js File
const WeatherDisplay = ({weather}) => {
console.log(weather.current.temperature)
return (
<h1>{weather.current.temperature}</h1>
)
}
export default WeatherDisplay
Having issues display the data when i use {weather.current.temperature}, it keeps giving me an error pointed at temperuture saying it isnt defined but its apart of the data
You are passing weather.current as props. While the child component is expecting weather as prop. So, what you end up doing is weather.current.current.temperature which is undefined because it doesn't exist. Just pass weather to the child prop.
Make this change when calling your child component.
<WeatherDisplay weather={weather}/>

React useEffect Hook not Triggering on First Render with [] Dependencies

I'm getting data via an Axios GET request from a local API and trying to save the data in a Context Object.
The GET request works properly when I run it outside the Context Provider function. But when I put it within a UseEffect function with no dependencies - ie. useEffect( () => /* do something*/, [] )the useEffect hook never fires.
Code here:
import React, { createContext, useReducer, useEffect } from 'react';
import rootReducer from "./reducers";
import axios from 'axios';
import { GET_ITEMS } from "./reducers/actions/types";
export const ItemsContext = createContext();
function ItemsContextProvider(props) {
const [items, dispatch] = useReducer(rootReducer, []);
console.log('this logs');
useEffect(() => {
console.log('this does not');
axios.get('http://localhost:27015/api/items')
.then(data => dispatch({type: GET_ITEMS, payload: data}))
}, [])
return (
<ItemsContext.Provider value={{items, dispatch}}>
{ props.children }
</ItemsContext.Provider>
);
}
export default ItemsContextProvider;
I never see 'this does not' in the console (double and triple checked). I'm trying to initialise the context to an empty value at first, make the GET request on first render, and then update the context value.
I'd really appreciate any help on what I'm doing wrong.
EDIT - Where Context Provider is being rendered
import React from 'react';
import AppNavbar from "./Components/AppNavbar";
import ShoppingList from "./Components/ShoppingList";
import ItemModal from "./Components/ItemModal";
//IMPORTED HERE (I've checked the import directory is correct)
import ItemsContextProvider from "./ItemsContext";
import { Container } from "reactstrap"
import "bootstrap/dist/css/bootstrap.min.css";
import './App.css';
function App() {
return (
<div className="App">
<ItemsContextProvider> //RENDERED HERE
<AppNavbar />
<Container>
<ItemModal />
<ShoppingList /> //CONSUMED HERE
</Container>
</ItemsContextProvider>
</div>
);
}
export default App;
I have it being consumed in another file that has the following snippet:
const {items, dispatch} = useContext(ItemsContext);
console.log(items, dispatch);
I see console logs showing the empty array I initialised outside the useEffect function in the Context Provider and also a reference to the dispatch function.
I had the same problem for quite a while and stumbled upon this thred which did not offer a solution. In my case the data coming from my context did not update after logging in.
I solved it by triggering a rerender after route change by passing in the url as a dependency of the effect. Note that this will always trigger your effect when moving to another page which might or might not be appropriate for your usecase.
In next.js we get access to the pathname by using useRouter. Depending on the framework you use you can adjust your solution. It would look something like this:
import React, { createContext, useReducer, useEffect } from 'react';
import rootReducer from "./reducers";
import axios from 'axios';
import { GET_ITEMS } from "./reducers/actions/types";
import { useRouter } from "next/router"; // Import the router
export const ItemsContext = createContext();
function ItemsContextProvider(props) {
const [items, dispatch] = useReducer(rootReducer, []);
const router = useRouter(); // using the router
console.log('this logs');
useEffect(() => {
console.log('this does not');
axios.get('http://localhost:27015/api/items')
.then(data => dispatch({type: GET_ITEMS, payload: data}))
}, [router.pathname]) // trigger useEffect on page change
return (
<ItemsContext.Provider value={{items, dispatch}}>
{ props.children }
</ItemsContext.Provider>
);
}
export default ItemsContextProvider;
I hope this helps anyone in the future!
<ItemsContextProvider /> is not being rendered.
Make sure is being consumed and rendered by another jsx parent element.

Categories

Resources