How convert date properly in ReactJS frontend side - reactjs

My API return some array of objects with LocalDate string and when I get it in my frontend side I want to convert it from state to JS Date object with timezone.
I tried to do it in componentDidMount() function after fetching API data but after setting state react developer tool shows me that dateList state is udentified.
DateComponent
class DateComponent extends Component {
constructor(props) {
super(props);
this.state = {
dateList: [],
id: this.props.id,
}
}
componentDidMount() {
DateService.retrieveProfile(this.state.id)
.then(response => {
this.setState({
dateList:this.convertDatesToTimezoneUTC(response.data.dateList)
})
})
}
convertDatesToTimezoneUTC(dateList) {
dateList.map((value) => (
value.date = new Date(value.date),
)
);
}
render() {
let {dateList, id} = this.state;
return (
<div>
<Formik
enableReinitialize
initialValues={{dateList}}>
<FieldArray name="dateList"
render={() => (
<div>
{dateList.map((value, index) => (
<div className={"m-3 form-row"} id={value.id} key={index}>
<Field className="form-control col-md-2 mr-2" type="text"
name={`dateList[${index}].date`} readOnly/>
</Formik>)
}
</div>
)
}
}
export default DateComponent;
Model
dateList[{
id:"some id"
date:"2019-08-16"
}]
Without convert convertDatesToTimezoneUTC() function everything is alright and return data properly. Did I miss something ?

I guess you forgot to return from the convertDatesToTimezoneUTC():
convertDatesToTimezoneUTC(dateList) {
return dateList.map((value) => (
value.date = new Date(value.date),
));
}

You need to return your newly computed array from convertDatesToTimezoneUTC function, also you need to only change the date and keep rest of the object properties as it is,
convertDatesToTimezoneUTC = (dateList) => {
return dateList.map((value) => ({
...value,
date : new Date(value.date),
})
)
}

Related

How to setState to answer from APi and use map

Im trying to create recipes searcher. In App.js I receive query from search input from another component and I want to setState to answer from APi. Console.log from callback in setState shows updated state but the state is not updated. I need setState updaed so I can use map on it and display list of recipes in render. It gives me error map is not a function because this.state.recipesList is still empty. Anyone can help me ?
class App extends Component {
state = {
query: "",
recipesList: []
};
getQuery = query => {
const key = "2889f0d3f51281eea62fa6726e16991e";
const URL = `https://www.food2fork.com/api/search?key=${key}&q=${query}`;
fetch(URL)
.then(res => res.json())
.then(res => {
this.setState(
{
recipesList: res
},
() => {
console.log(this.state.recipesList);
}
);
});
console.log(this.state.recipesList);
};
render() {
const test = this.state.recipesList.map(item => {
return (
<div className="recispesList">
<h1>{item.title}</h1>
</div>
);
});
return (
<div className="App">
<Search query={this.getQuery} />
<div className="contentWrapper">{}</div>
</div>
);
}
}
Search component:
class Search extends Component {
state = {
searchValue: ""
};
handleChange = val => {
let searchValue = val.target.value;
this.setState({
searchValue
});
};
handleSubmit = e => {
e.preventDefault();
this.setState({
searchValue: ""
});
this.props.query(this.state.searchValue);
};
render() {
return (
<div className="searchWrapper">
<form onSubmit={this.handleSubmit}>
<input onChange={this.handleChange} value={this.state.searchValue} />
<button />
</form>
</div>
);
}
}
export default Search;
It seems that instead of directly assigning the whole response to recipesList:
this.setState(
{
recipesList: res
},
() => {
console.log(this.state.recipesList);
}
);
you need to get recipes array first via res.recipes:
this.setState(
{
recipesList: res.recipes
},
() => {
console.log(this.state.recipesList);
}
);

How render components after fetching data

I have a container called "Recetas" (Receipes) which has inside a component called "Showcase". The idea is that the user can create a request in "Recetas" and when the data is fetched, the container will re-render and "Showcase" will update with the new data.
I'm saving the request in the state of "Recetas" and passing the data to "Showcase" with props.
The problem is that the render is being execute before I receive the new data. So I'm always showing "old" data. Is there any way I can put on hold the render until I've received the data? Or how can I solve it?
class Recetas extends Component {
state = {
loading: false,
data: [],
maxResult: 12,
minResult: 0,
query: 'burger',
appId: 'xxxxxx',
appKey: 'xxxxx'
}
componentDidMount() {
this.fetchData();
}
async fetchData() {
this.setState({loading: true});
console.log('fetching ...');
try {
const request = `https://api.edamam.com/search?q=${this.state.query}&app_id=${this.state.appId}&app_key=${this.state.appKey}&from=${this.state.minResult}&to=${this.state.maxResult}`;
console.log('request: ', request);
const result = await axios(request);
this.setState({ data: result.data.hits, loading: false });
} catch (error) {
console.log(error);
}
}
queryHandler = value => {
this.setState({
query: value
});
this.fetchData();
}
render() {
return (
<div className={classes.Recetas}>
{console.log('actualState: ', this.state)}
<SearchInput
query={this.state.query}
queryHandler={(value) => this.queryHandler(value)}/>
<Showcase
data={this.state.data}
loading={this.state.loading}/>
</div>
);
}
Showcase component
const showcase = props => {
const spinner = <Spinner />;
const recetas = props.data.map((elem, index) => {
return <Receta key={index} title={elem.recipe.label} url={elem.recipe.image} />
});
console.log('[Showcase] props.data: ', props.data);
return (
<div className={classes.Showcase}>
{props.loading ? spinner : recetas}
</div>
);
}
In case the Function Component not watch the props change. You should move the spinner to the container component.
Recetas.js
render() {
...
const {loading, data} = this.state
return (
...
<div className={classes.Recetas}>
{loading ? < Spinner /> : <Showcase data={data} />}
</div>
);
}
//////////////
ShowCase.js
const showcase = props => (
<div className={classes.Showcase}>
{
props.data.map(({recipe: {label, imgage}}, index) => <Receta key={index} title={label} url={image} />)
}
</div>
)
And using Destructuring_assignment for shorter code.
Finally I managed to solve it adding a setTimeout function which execute the fetch 500 ms after updating the state in 'queryhandler' method.

Setting State Array and Appending Value on Update

I'm still learning about state and lifecycle with ReactJS and have run into a scenario where I have a form that on submit should save the form value and then append the returned JSON object to the end of an array which would re-render the component storing the original array.
With my current setup, I have the components setup and form submit with returned JSON object, but the state contains an empty array rather than the object spread {...comment} and it doesn't look like the setState is updating component, but that could be due to the empty array mentioned before. Can anyone point me in the right direction?
Comment:
import React from 'react';
import fetch from 'node-fetch';
//record Comment - Comment Form Handle POST
class CommentForm extends React.Component {
constructor(props){
super(props);
this.state = {
value: '',
comments: []
};
this.onChange = this.onChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
postComment(comment, recordId, csrfToken) {
var body = { comment: comment };
var route = 'http://localhost:3000/record/' + recordId + '/comment';
fetch(route,
{
method: 'POST',
body: JSON.stringify(body),
headers: {
'X-CSRF-Token': csrfToken,
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
.then(res => {
return res.json();
})
.then(data => {
console.log(data);
let commentsArr = this.state.comments;
this.setState({comments: commentsArr.concat(data)});
})
.catch(err => {
console.log(err);
});
}
onChange(e){
this.setState({
value: e.target.value
});
}
handleSubmit(e){
e.preventDefault();
this.postComment(this.state.value, this.props.recordId, this.props.csrf);
}
render(){
return (
<div className="record-comment__form">
<div className="row">
<form action={"/record/" + this.props.recordId + "/comment"} method="post" onSubmit={this.handleSubmit}>
<input type="hidden" name="_csrf" value={this.props.csrf}/>
<textarea name="comment" className="record-comment__form-text-area" onChange={e => this.setState({ value: e.target.value })} value={this.state.value}></textarea>
<button type="submit" className="record-comment__form-button" disabled={!this.state.value}>Comment</button>
</form>
</div>
</div>
)
}
}
//record Comment - Comment
const Comment = props => {
return (
<div className="row">
<div className="col-md-12">
<h5>{props.user_id}</h5>
<h4>{props.comment}</h4>
<h3>{props.synotate_user.fullNameSlug}</h3>
</div>
</div>
)
}
//record Comment - Container
export default class Comments extends React.Component {
render() {
return (
<div className="record-comment-container">
<CommentForm recordId={this.props.recordId} csrf={this.props.csrf}/>
{ this.props.record_comments.map((comment, i) =>
<Comment {...comment} key={this.props.recordCommentId}/>
)}
</div>
);
}
}
Record (Parent component)(Where Comment is being set):
//GET /api/test and set to state
class RecordFeedContainer extends React.Component{
constructor(props, context) {
super(props, context);
this.state = this.context.data || window.__INITIAL_STATE__ || {records: []};
}
fetchList() {
fetch('http://localhost:3000/api/test')
.then(res => {
return res.json();
})
.then(data => {
console.log(data);
this.setState({ records: data.record, user: data.user, csrf: data.csrfToken });
})
.catch(err => {
console.log(err);
});
}
componentDidMount() {
this.fetchList();
}
render() {
return (
<div className="container">
<h2>Comments List</h2>
<RecordFeed {...this.state} />
</div>
)
}
};
//Loop through JSON and create Record and Comment Container Component
const RecordFeed = props => {
return (
<div>
{
props.records.map((record, index) => {
return (
<div className="row">
<div className="col-md-6 col-md-offset-3 record-card">
<RecordCard {...record} key={record.recordIdHash} user={props.user} />
<Comments {...record} key={index} recordId={record.recordIdHash} csrf={props.csrf}/>
</div>
</div>
);
})
}
</div>
)
}
Your problem is that when rendering <Comments>, the this.props.record_comments is not the comments you've updated in the state of the <CommentForm> component. Each component has it's own internal state.
You need to pass the state along to your <Comments> component. You will need to move your state up to the top level or use a state management system like Redux which will allow you to access a shared state which could contain your comments array.
From the top level component you could manage the state there, like so:
this.state = {
comments: [],
// other shared state
};
You can pass along an update comments function, named for example updateCommentsFunc() to <CommentForm> like so:
<CommentForm updateComments={this.updateCommentsFunc} recordId={this.props.recordId} csrf={this.props.csrf}/>
Which will allow you to pass the updated comments back up to the parent component via something like:
const updateCommentsFunc = (newComments) => {
this.setState({comments: [...this.state.comments, newComments]});
}
Your postComment() function doesn't appear to be properly bound to your enveloping <CommentForm/> component's this. As a result; calling this.setState() from within the function isn't really doing anything.
Try binding it within your constructor method.
constructor(props) {
// ...
this.postComment = this.postComment.bind(this)
}
Or by declaring it using an arrow function.
postComment = (comment, recordId, csrfToken) => {
// ...
}
See this article for more info on React binding patterns.

Renaming the array elements and updating it to the reducers using reactjs

Hi Im trying to display the array with textbox for each element as shown in the image. The issue faced by me is when I enter the new name in textbox the same name is assigned for all the elements, so how do I overcome this issue, and save individual name for each current name. So this help in updating the database with new name.
class TablebackupName extends Component {
constructor(props) {
super(props);
this.state = {
tName: [pokemon, XXX, Batman],
bName: [newname : ''],
};
this.onNameEdited = this.onNameEdited.bind(this);
}
onNameEdited(event) {
this.state.bName.newname = event.target.value;
this.setState({ bName: this.state.bName });
};
render() {
return (
<div>
{this.state.tName.map(x =>
<input type="text" label={x} key={x.toString()} value={this.state.bName.newname} onChange={this.onNameEdited} />)}
</div>
);
}
}
Don't mutate state directly:
//wrong
this.state.bName.newname = event.target.value;
this.setState({ bName: this.state.bName });
//right
this.setState({ bName: {newName: event.target.value} });
You set the same state property for all of your map elements, so the state is shared.
I'd tackle it by setting defaultValue and then - on each update, just update the tName array with the new values.
export default class TablebackupName extends React.Component {
constructor(props) {
super(props);
this.state = {
tName: ["pokemon", "XXX", "Batman"]
};
this.onNameEdited = (index) => (event) => {
//using map to keep everything immutable
let element = event.target;
this.setState({
tName: this.state.tName.map((val, i) => i === index ? element.value : val)
})
};
}
// creating a higher order function to get the right index for later use.
render() {
return (
<div>
{this.state.tName.map((x, index) => {
return (
<div key={index}>
<label>{x}</label>
<input type="text" defaultValue="" onKeyUp={this.onNameEdited(index)}/>
</div>)
})}
</div>)
}
}
webpackbin: https://www.webpackbin.com/bins/-Ko7beSfn-FR__k94Q3k

Rendering data from an array of objects

I have integrated the search filter and retrieved the json data( array of objects ) using the post method based on the search term. The json output is given below:
[
{"taxi_code":"CT0001","class":"0"},
{"taxi_code":"CT0002","class":"0"},
{"taxi_code":"CT0003","class":"0"}
]
But the json data is not rendered to the DisplayTable component even using map method. What i did wrong?? Using console.log(<DisplayTable data={queryResult} />), i got this type of output: Object { props={...}, _store={...}, $$typeof=Symbol {}, more...}
class Results extends Component
{
constructor(props){
super(props);
this.state={searchTerm:''};
}
render(){
return(<div id="results" className="search-results">
{this.props.data}
<SearchInput className="search-input" onChange={e=>this.searchUpdated(e)} />
</div>);
}
searchUpdated (e) {
this.setState={searchTerm: e};
var queryResult;
axios.post(config.api.url + 'public/getAttributesbyname', {'searchTerm':e,'name':this.props.data})
.then(response => {
var queryResult = response.data;
render()
{
return (<DisplayTable data={queryResult}/>);
}
})
.catch(response => {
});
}
}
class DisplayTable extends Component
{
render()
{
return this.props.data.map((alldata)=> {
return <div className="station">{alldata.taxi_code}</div>;
});
}
}
You have several mistakes in your code,
You can't return values from asynchronous function(axios.post)
this.setState is method and you have to call it this.setState() but not assign value to it
I think in this case you don't need handle state from input field searchTerm, you can get value from input and use it., however you should handle state for data which you get from server.
I've refactored your example, and now it looks like this
class Results extends Component {
constructor(props) {
super(props);
this.state = { data: [] };
}
render(){
return <div id="results" className="search-results">
<DisplayTable data={ this.state.data } />
<SearchInput
className="search-input"
onChange={ e => this.searchUpdated(e) }
/>
</div>;
}
searchUpdated (e) {
axios
.post(config.api.url + 'public/getAttributesbyname', {
searchTerm: e.target.value,
name: this.props.data
})
.then(response => {
this.setState({ data: response.data });
})
.catch(response => {});
}
}
class DisplayTable extends Component {
render() {
const stations = this.props.data.map((alldata, index) => {
return <div className="station" key={ index }>{ alldata.taxi_code }</div>;
});
return <div>{ stations }</div>
}
}

Resources