How can I dynamically rerender my api to my webpage? - reactjs

So I have this api and I am making a get request in my ComponentDidMount() to dynamically render it to my page and it works. The issue I am facing is when I make a post request to add items to the list, it does not show on my webpage unless I refresh it. The backend is my data.json so I don't know if that is the problem but essentially when I make a post request, I am adding data to my data.json and I want that to rerender on my page without me refreshing it.
componentDidMount() {
axios.get("/api/workboard")
.then(res => {
res.data["boardLists"].map((item, key) => {
// console.log(Object.keys(item)[0])
this.setState(prevState => ({
data: [...prevState.data, item],
titles: [...prevState.titles, Object.keys(item)[0]]
}))
})
// console.log(this.state.titles)
// console.log(this.state.data)
}).catch(err => console.log(err))
}
addListItemHandler = () => {
axios({
method: 'post',
url: 'api/workboard/0/list',
data: {
title: "Untitled" ,
description: "No Description"
}
})
.then(res => {
console.log(res)
})
.catch(err => console.log(err));
}
render() {
let board = this.state.data.map((item, key) => {
return <WorkBoardContainer
key={key}
title={item[this.state.titles[key]]["title"]}
listItems={item[this.state.titles[key]]["list"].map((i) => {
return i["title"]
})}
/>
})
return (
<div className={classes.App}>
<AddButton addListItemHandler={this.addListItemHandler}/>
{board}
</div>
);
}

Try moving the fetching part as a seperate function and call it again once the post request is done.
componentDidMount() {
// fetch data when component is mounted
this.fetchData();
}
fetchData = () => {
axios.get("/api/workboard")
.then(res => {
res.data["boardLists"].map((item, key) => {
this.setState(prevState => ({
data: [...prevState.data, item],
titles: [...prevState.titles, Object.keys(item)[0]]
}))
})
}).catch(err => console.log(err))
}
addListItemHandler = () => {
axios({
method: 'post',
url: 'api/workboard/0/list',
data: {
title: "Untitled" ,
description: "No Description"
}
})
.then(res => {
console.log(res);
// fetch data again once post is done.
this.fetchData();
})
.catch(err => console.log(err));
}

Related

State is being changed, but display not updating

I've seen quite a few posts about this, but none of the solutions seem to work for me.
I have a render method that is being mapped to list the different sections stored in a state(this part works as expected):
render() {
return (
<ThemeProvider theme={theme}>
<Div>
<hr />
<div>
{this.state.sections.map(section => (
<div
className="mb-3"
key={section.section_title}
>
<h3>{section.section_title}</h3>
</div>
))}
</div>
)
}
However, I have a modal that allows you to create a new section by giving it a name and clicking submit. That does create it and add it to my database table as expected. But, then when I run the method to pull that data down, and change the state to include the new section, it works, and does indeed change the state to include the new section. But it does not update the display unless I reload the page. Can anyone see why?
getProjectSections(projId) {
fetch(API_URL + `/billingcalculator/sections/distinct/${projId}`)
.then((res) => {
if (!res.ok) {
throw new Error()
}
return res.json()
})
.then((result) => {
let listedSections = [...result];
this.setState({ sections: listedSections });
})
.catch((error) => {
console.log(error);
})
}
the getProjectSections() runs when you click the submit button a creating a new section which runs this:
handleSectionCreateSave() {
fetch(API_URL + `/billingcalculator/section/create`, {
method: "PUT",
body: JSON.stringify({
projectId: this.props.billProjId,
sectionTitle: this.state.newSectionTitle
}),
headers: { "Content-Type": "application/json" },
})
.then((res) => {
if (!res.ok) {
throw new Error();
}
return res.json();
})
.then((data) => console.log(data))
.catch((err) => console.log(err))
.then(this.getProjectSections(this.props.billProjId))
.then(this.setState({ showSectionModal: false }))
.catch((err) => console.log(err));
}
You are calling state updates before request happens:
handleSectionCreateSave() {
fetch(API_URL + `/billingcalculator/section/create`, {
method: "PUT",
body: JSON.stringify({
projectId: this.props.billProjId,
sectionTitle: this.state.newSectionTitle
}),
headers: { "Content-Type": "application/json" },
})
.then((res) => {
if (!res.ok) {
throw new Error();
}
return res.json();
})
.then((data) => console.log(data))
.catch((err) => console.log(err))
// you called these function now, instead after fetch
// use () =>
.then(() => this.getProjectSections(this.props.billProjId))
.then(() => this.setState({ showSectionModal: false }))
.catch((err) => console.log(err));
}

call function synchronously in reactjs

I want to call function only after previous function gets executed. I tried with promises but its not working,I also tried with async await but the last function is getting executed.After execution of first function its state value i want to pass to next function and so on.Please help me in this.Thanks in advance.
handleAllFunctionsOnClickPayLater() {
let promise = Promise.resolve();
promise
.then(() => this.handleGuestLogin())
.then(() => setTimeout(this.handleAddress(),1000))
.then(() => setTimeout(this.handlePayLater(),2000))
}
handleGuestLogin() {
const UserDetails = {
name: this.state.name,
email: this.state.email,
mobile: this.state.number
}
fetch(api,{
method : 'POST',
body: JSON.stringify(UserDetails)
})
.then(res => res.json())
.then(data => {
return this.setState({
cid: data.Data.cid
},() => {console.log(this.state.cid)})
})
}
handleAddress() {
var address_details = {
cid:this.state.cid
...other details
}
fetch(api,{
method : 'POST',
body: JSON.stringify(address_details)
})
.then(res => res.json())
.then(data => {
console.log("address added in db customer_address",data);
return this.setState({
address_id: data.address_id,
})
}
handlePayLater = () => {
var bookingDetails = {
cid: this.state.cid,
address_id: this.state.address_id
}
fetch(api,{
method : 'POST',
body : JSON.stringify(bookingDetails)
})
.then(res => res.json())
.then(data => {
return this.setState({bookingId:data.booking_id});
}
Assuming handleAddress, handleGuestLogin and handlePayLater return promises, you can use an async/await function
synchronousPromises = async () => {
try {
const handleGuestLoginResult = await this.handleGuestLogin();
const handleAddressResult = await this.handleAddress();
const handlePayLaterResult = await this.handlePayLater();
} catch (error)
{
return reject(error); //will cause .catch to fire
}
return resolve([
handleGuestLoginResult,
handleAddressResult,
handlePayLaterResult
]); //will cause .then to fire
}
since synchronousPromises is an async function, it itself returns a promise. to use it, you can call it as
callSyncronousPromises = () => {
synchronousPromises()
.then(success => {
//handle success
})
.catch(error => {
//handle error
}
}

React: How do i trigger a function that contains a lifecycle method, from another component?

I have two components Navbar and Modal.
The Navbar contains a function called displayData() that contains a lifecycle method called componentDidMount().
So, In Modal Component I have a function that updates a data using FetchApi, On save of the data I need to trigger the displayData() function, I tried to pass it down as props, but it still doesn't get triggered
What am I doing wrong here? Any help would be appreciated
Navbar Code That contains the componentDidMount
//Displaying the Data
componentDidMount() {
this.displayData();
}
displayData() {
fetch("/user")
.then(data => data.json())
.then(data => {
console.log(data);
this.setState({
userArray: data
});
});
}
Modal Code where I need to trigger the displayData()
updateBtn = e => {
fetch(`/user/${id}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(newData)
})
.then(data => {
return data.json();
})
.then(data => {
console.log(data);
this.props.displayData(this); // This is where i'm trying to trigger the function
})
.catch(err => {
console.log(err);
});
};
Calling Modal from Navbar. I'm Passing displayData() in the display as props
<Modal
data={this.state.username}
dataone={this.state.email}
id={this.state.id}
close={this.closeModel}
log={this.logChange}
display={this.displayData}
/>
// Navbar Component
constructor(props) {
this.state = { isFetchComplete: false }
}
updateBtn = e => {
fetch(`/user/${id}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(newData)
})
.then(data => {
return data.json();
})
.then(data => {
this.setState({ isFetchComplete: true })
})
.catch(err => {
console.log(err);
});
};
{ this.state.isFetchComplete &&
<Modal
data={this.state.username}
dataone={this.state.email}
id={this.state.id}
close={this.closeModel}
log={this.logChange}
/>
}
// Modal Component
constructor (props) {
super(props)
this.state = {}
this.displayData = this.displayData.bind(this)
}
//Displaying the Data
componentDidMount() {
this.displayData();
}
displayData() {
fetch("/user")
.then(data => data.json())
.then(data => {
console.log(data);
this.setState({
userArray: data
});
});
}
Hope that helps!!
Define displayData() using arrow function so that it's lexically binded to Navbar:
displayData = () => {
fetch("/user")
.then(data => data.json())
.then(data => {
console.log(data);
this.setState({
userArray: data
});
});
}
And then in your Modal code, you call this.props.display since you passed down the prop through: display={this.displayData}
.then(data => {
console.log(data);
this.props.display();
})

problem with fetch in componentDidMount()

my list of users is undefined when i try to console.log it.
Maybe i didn't get something ?
I'd like to get my list of users from my api who works (tested with postman) and put it into the console next i'd like to map my users to show it on the app
class Test extends Component {
constructor(props) {
super(props);
this.state = {
users: [],
}
}
componentDidMount() {
console.log("component did mount");
fetch("/user/list")
.then(res => {
return res.json();
})
.then(users =>
this.setState({users}, () =>
console.log("list of users => " + users)));
}
render() {
return (
<div className="form">
<ul>
{this.state.users.map((user) =>
<li key="user._id">{ user.name }</li>
)}
</ul>
</div>
);
}
} export default Test;
Thanks for help !
You are calling res.json() rather than returning res.json() from the first then on your fetch call
I've found this pattern to be helpful:
fetch(url)
.then(res => res.ok ? res.json() : Promise.reject())
As your code is now, users (the parameter in the second then would be undefined, because you are not returning anything from the first then
you have to return the res.json() to use it in the next .then()
.then(res => {
res.json();
})
should be
.then(res =>
res.json();
)
Or
.then(res => {
return res.json();
})
https://javascript.info/promise-chaining
You should be passing your res into res.json() and returning the results into your state.
componentDidMount() {
console.log("component did mount");
fetch("/user/list")
.then(res => res.json())
.then(users =>
this.setState(users,
() => {
console.log("list of users => " + users)
})
);
}
Michael Jasper response help me so much!
I found that fetch with GET method does not work if we pass any request body.
the full example is here
https://github.com/alexunjm/todo-list-react
const buildRequestOptions = ({
method = "GET",
raw = null, // I had my error here!, with GET raw need to be null
customHeaders = {name: 'value'},
}) => {
var myHeaders = buildHeaders(customHeaders);
var requestOptions = {
method,
headers: myHeaders,
body: raw,
redirect: "follow",
};
return requestOptions;
};
const listTasks = () => {
const url = `${uriBase}/task/sample`;
const requestOptions = buildRequestOptions({
customHeaders: { "Content-Type": "application/json" },
});
return fetch(url, requestOptions);
}
const asyncFn = ({
promiseToWait,
pendingFn,
successFn,
errorFn,
}) => {
return (dispatch) => {
dispatch(pendingFn());
promiseToWait
.then((res) => {
if (res.ok) {
return res.json();
}
// handled from server status 422 and 401
if (res.status === 422) {
// error message on body from server
return res.json();
}
if (res.status === 401) {
// custom error message hardcoded
return {errors: {action: 'no authorized'}}
}
console.log("http response no controlled", res);
return Promise.reject();
})
.then((body) => {
if (body.errors) {
const errors = Object.keys(body.errors).map(
(key) => key + " " + body.errors[key]
);
dispatch(errorFn(errors.join("; ")));
} else {
dispatch(successFn(body));
}
return body;
})
.catch((error) => {
console.log("error", error);
dispatch(errorFn("Unavailable server connection"));
});
};
};
const queryTasks = () => {
return asyncFn({
promiseToWait: listTasks(),
pendingFn: apiPending,
successFn: apiSuccessList,
errorFn: apiError,
});
}

Fetch and store data from multiple url for the sections data of SectionList

I would like to use the SectionList in react native.
export default class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
dataSource: [
{
title: 'New List', data: []
},
{
title: 'Old list', data: []
},
]
}
}
render() {
return (
<SectionList
style={styles.SectionContainer}
sections={this.state.dataSource}
renderSectionHeader={this._renderSectionHeader}
renderItem={this._renderItem}
keyExtractor={(item) => item.id}
/>
)
}
}
Each section's data can be fetched by separate url, and they basically have the same json data:
getNewList() {
const url = website + '/api/new-list/';
return fetch(url)
.then((res) => res.json())
.catch((err) => console.log(err))
},
getOldList() {
const url = website + '/api/old-list/';
return fetch(url)
.then((res) => res.json())
.catch((err) => console.log(err))
}
How can fetch and store both the response data for the dataSource of SectionList?
Sounds like you need to fire off multiple promises and wait for all to complete. Axios has an awesome .all helper utility that lets you pass in an array of promises and then waits for all of them to finish before running resolving:
function getUserAccount() {
return axios.get('/user/12345');
}
function getUserPermissions() {
return axios.get('/user/12345/permissions');
}
axios.all([getUserAccount(), getUserPermissions()])
.then(axios.spread(function (acct, perms) {
// Both requests are now complete
}));
You can also do something close to this using native Promises:
getLists(){
this.getListData().then( responses => {
this.setState({
dataSource: [
{
title: 'New List', data: responses[0]
},
{
title: 'Old list', data: responses[1]
},
]
)};
});
}
getListData(){
return new Promise( (resolve, reject) => {
let completes = 0;
let responses = [];
let url = website + '/api/new-list/';
fetch(url)
.then((res) => {
responses.push(res.json());
completes++;
if(completes === 2){
resolve(responses);
}
})
.catch((err) => console.log(err));
url = website + '/api/old-list/';
fetch(url)
.then((res) => {
responses.push(res.json());
completes++;
if(completes === 2){
resolve(responses);
}
})
.catch((err) => console.log(err))
});
}

Resources