How to update React component when async prop promise resolves? - javascript

Lets say I have a component that renders a PivotGrid component. This PivotGrid takes some data, an array of objects.
function My_component(props) {
return <PivotGrid dataSource={props.data} />
}
However, the data prop that I want to pass in is the result of an async function.
const get_data = async () => {
return await o(url, options).get('foo').query({})
}
ReactDOM.render(<My_component data={get_data()}/>, document.getElementById('root'));
What happens is that the component renders before the promise from get_data() is resolved, and the PivotGrid has no data.
What I would like to happen is for the component to re-render when the promise resolves and actually returns data. I've tried variations of React's useState() to treat props.data as a state variable, so that when the promise returns the state would change the the component would update. But this has not worked yet.
const [gridData, setGridData] = useState(props.data);
props.data.then((r) => {
setGridData(props.data)
})
Attempts like the above all fail. What is the best way to achieve this functionality, where the component re-renders when prop.data resolves and actually holds the data I want?

Using hooks and the container component for My_component should work.
my-component-container.js:
import React, {useState, useEffect} from 'react'
import My_component from './my-component'
export default () => {
const [data, setData] = useState(null)
useEffect(async () => {
const fetchData = async () => {
const result = await o(url, options).get('foo').query({})
setData(result);
};
fetchData();
}, [])
return <My_component dataSource={data} />
}
In your entry point:
import My_component_container from './my-component-container'
ReactDOM.render(<My_component_container />, document.getElementById('root'))

Why not change to a stateful component like below
class My_component extends React.Component {
state = {};
componentDidMount(){
this.get_data()
}
get_data = async () => {
const data = await o(url, options).get('foo').query({});
this.setState({ data });
}
render() {
const { data } = this.state;
return <PivotGrid dataSource={data} />
}
}

Related

How to fetch API as soon as page is loaded in React?

Whenever I visit a page it should automatically fetch the API
import React from 'react'
const Component = () => {
fetch("api url").then((res) => console.log(res))
return (
<div>comp</div>
)
}
export default Component
It is very simple using react hook use effect please learn basics of useffect hook on react docs or any youtube tutorial and as for the answer
import React, { useEffect } from 'react'
const comp = () => {
useEffect(() => {
fetch("api url").then((res)=>console.log(res))
}, [])
return (
<div>comp</div>
)
}
export default comp
here empty dependency means every time page loads only once
use the useEffect for this.
The useEffect method will execute the passed callback on the mount of the component and on every time one of the dependency array parameters is changed. therefore:
const Comp = () => {
useEffect(() => {
fetch("api url").then((res)=>console.log(res))
}, []);
return (
<div>comp</div>
)
}
Will make the callback to fire only once (because the empty dependency array) on the component mount.
You should use the useEffect Hook in your principal component like app.js
import React, {useEffect} from 'react'
useEffect(() => {
fetch("api url").then((res)=>console.log(res))
}, []);
Be careful, this manipulation can consume a lot of resources (a lot of data to fetch etc.)
Thery
import React, { useState, useEffect } from 'react'
const Comp = () => {
const [ data, setData ] = useState([]);
const getData = async () => {
const res = await fetch("api url");
const data = await res.json();
setData(data)
}
useEffect(()=>{ getData() },[]);
return (
<>
<div>comp</div>
// dispaly your data here from data state
</>
)
}
export default Comp;
Fetch and use data with useState
const initialValue = {};
const comp = () => {
const [data, setData] = useState(initialValue);
useEffect(() => {
let ignore = false;
const fetchData = async () => {
const res = fetch("api url");
if (ignore) { return; }
setData(res.json())
return () => {
ignore = true;
}
}
, [])
return (
<div>comp {data.prop}</div>
)
}
More on working with state
More about useEffect life cycle
Hope it helps
You don't need to use the API function like this, it will be called continuously, you need to use useEffect hook, when your component reloads useEffect will be called, and you can learn about the useEffect dependency here,
import React, { useEffect, useState } from 'react'
const comp = () => {
const [data, setData] = useState([]);
useEffect(() => {
fetch("api url").then((res)=> {
console.log(res)
setData(res)
} )
}, [])
return (
// use data state to show the data here
<div>comp</div>
)
}
export default comp;

How to manage with data from fetch promise?

I am beginner in JS and React.
I have a problem:
import React from "react";
import JsonApi from "../../services/jsonApi";
const UserPage = () => {
const jsonApi = new JsonApi(); //it is my class which has methods
//to manage with data(get,post,etc);
const user = jsonApi.getUser(); //returns promise,but i need an object with data!
//promise has such view:
//[[Prototype]]: Promise
//[[PromiseState]]: "fulfilled"
//[[PromiseResult]]: Object !!!!i need this data!!!!
console.log(user); //Promise.
/* i know that a i can do so:
user.then((data) => console.log(data));
but,using this way,i can only log!But i need an object with data!
*/
return (
<div className="app">
<h1>{user.name}</h1>
<p>Here are info about users!</p>
</div>
);
};
export default UserPage;
I understand that i need to use await before const user = jsonApi.getUser();
but we can do that only inside async functions.
So,i tried to do that: const UserPage = async () => { }
but i had a mistake:
In order to perform side effects in react you should consider using useEffect hook. After the effect you need to store the data retrieved in react state by using the useState hook. In the end your code would look like below:
import React, { useState, useEffect } from "react";
import JsonApi from "../../services/jsonApi";
const UserPage = () => {
const [user, setUser] = useState(null);
useEffect(() => {
const jsonApi = new JsonApi();
jsonApi.getUser().then((user) => {
setUser(user);
});
}, []);
if (!user) return null;
return (
<div className="app">
<h1>{user.name}</h1>
<p>Here are info about users!</p>
</div>
);
};
export default UserPage;
Keep in mind that the user is not populated until you async getUser resolves, so you have to handle the case where user data are not yet present, either by rendering nothing (null) or by showing some loading state in between.

How can I update state after setting it in UseEffect?

I want to display a list of products in one of my components. I need to use UseEffect to get the results of a function that returns a promise. I then am setting state in UseEffect but am having trouble displaying the products. The state is getting updated but I am not able to render the results on the dom.
Here's the component:
import React, { useEffect, useState } from "react"
import styled from "styled-components"
import pullShopifyData from "../../functions/shopify"
export default function DataPreview(props)
const { apiKey } = props
const [data, setData] = useState([])
useEffect(() => {
pullShopifyData(apiKey).then(response => {
console.log(response.data.shop.products)
setData(response.data.shop.products)
})
}, [])
return (
<Wrapper>
{data.length > 0 ? data.map(product => (
<ProductWrapper>{product}</ProductWrapper>
)): <NoProductsWrapper>No Products Found</NoProductsWrapper>}
</Wrapper>
)
}
const Wrapper = styled.div``
const ProductWrapper = styled.div``
const NoProductsWrapper = styled.div``
Here's the output on the dom:
Move your data fetch function in to async wrapper function. Then just call it in useEffect.
useEffect(() => {
fetchSomething();
}, [])
const fetchSomething = async () => {
const response = await pullShopifyData(apiKey);
console.log(response.data.shop.products)
setData(response.data.shop.products)
}

Using .map() with useEffect and Api

I am trying to use the useEffect to grab some data from an API. I am succesful in grabbing the data but after I set my state and try to map through it I just get "Can't read map of undefined". I think the problem is that it's running my .map() code before it gets the response. i am just unsure of how to solve this
This is the api response:
data: {count: 87, next: "https://swapi.co/api/people/?page=2", previous: null, results: Array(10)}
Here is my code
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import './App.css';
import CharacterMap from './characterMap'
const App = () => {
let [getChars, setChars] = useState(0);
useEffect(() => {
axios.get(`https://swapi.co/api/people/`)
.then(res => setChars(res) )
},[]);
console.log(getChars.data.map((e) => e))
return (
<div className="App">
<CharacterMap info={getChars} />
</div>
);
}
export default App;
axios.get is an async function and you are trying to get the data outside of an async function which is no completed yet.
You could use useEffect with dependency array which is equal to componentDidUpdate to get the data.
Initialized the state with the same datatype that you expect, in this case we expect an array you initialized ith with empty array.
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import './App.css';
import CharacterMap from './characterMap'
const App = () => {
let [chars, setChars] = useState([]);
useEffect(async () => {
try{
let response = await axios.get(`https://swapi.co/api/people/`)
let data = await response.json();
setChars(data);
} catch(error) {
console.error(error.message);
}
},[]);
// If you want to access the updated state then use this.
useEffect(() => {
let newState = chars.map((e) => e); // map your state here
setChars(newState); // and then update the state
console.log(newState);
},[getChars]);
return (
<div className="App">
<CharacterMap info={chars} />
</div>
);
}
export default App;
The second useEffect hook trigger on each state update and so you can get the updated state here.
It will also trigger a re-render so you can also use the map in return statement;
Or you could update the data on axios response and then set the state. Recommended
useEffect(async () => {
try{
let response = await axios.get(`https://swapi.co/api/people/`)
let data = await response.json();
let newState = data.map((e) => e); // map your state here
setChars(newState); // and then update the state
console.log(newState);
} catch(error) {
console.error(error.message);
}
},[]);
Keep the default values as array
let [getChars, setChars] = useState([]);
you are setting data to array chars. instead of that set array(results) that you are getting in response.
As you defined let [getChars, setChars] = useState([]);
useEffect(async () => {
axios
.get(`https://swapi.co/api/people/`)
.then(res=> setChars(res.data.results))
.catch(err=> console.log(err))
},[]);

React Context : Get Data from API and call API whenever some events happens in React Component

I am new to React Context.
I need to call the API in react context to use its data throughout my react application. Also the same API needs to be called on some CRUD operation on various component of react application.
For now I am storing API data in redux which I don't want to store.
Here is what I have tried..
context.js File
import React, { useState, createContext,useEffect } from 'react';
import {getData} from './actionMethods';
const NewContext = createContext();
function newContextProvider(props) {
useEffect(async () => {
const {dataValue} = await getData()
console.log("Data " , dataValue)
}, [])
return (
<NewContext.Provider
value={{
state: {
},
actions: {
}
}}
>
{props.children}
</NewContext.Provider>
);
}
const newContextConsumer = newContext.Consumer;
export { newContextProvider, newContextConsumer, newGridContext };
actionMethods.js
export function getData() {
let config = getInstance('GET', `${prefix}/xyz/list`)
return axios(config).then(res => res.data).catch(err => {
console.log(err)
})
}
when any CRUD operation performs , I need to call the API from the context.js file to get the data from API and store in the context.
Any help would be great.
Thank You.
First we create the Context and pass it an initial value.
In order to fetch data and keep track of the returned value, we create a state inside the component. This component will manage the fetched data and pass it in the Context Provider.
To call an async function inside useEffect we need to wrap it and call it inside useEffect callback.
export const NewContext = createContext({
my_data: {} // Initial value
});
export const NewContextProvider = props => {
const [my_data, setMyData] = useState({});
useEffect(() => {
const fetchMyData = async () => {
const { dataValue } = await getData();
if (dataValue) {
setMyData(dataValue);
} else {
// There was an error fetching the data
}
};
fetchMyData();
}, []);
return (
<NewContext.Provider
value={{
my_data
}}
>
{props.children}
</NewContext.Provider>
);
};
To use this Context in a component we use the useContext hook. Remember that this component needs to be wrapped by the Provider we just created.
import React, { useContext } from "react";
import { NewContext } from "./NewContext"; // The file where the Context was created
export const MyComponent = props => {
const { my_data } = useContext(NewContext);
return //...
};
Let me know if something is not clear.

Categories