this.setState() not updating state with passed array - javascript

I am passing an array which contains the following credentials.:
//Snippet variable contains an array which looks like this
{
details: test,
_id: "5d9d0b506ee7d03a54d8b1f6",
name: "TEST GREEN",
content: "<div style="background-color:green; height:100px; width:100px;"></div>",
}
The array is recieved by a function, the function updates the state.
The state looks like this:
constructor(props) {
super(props)
this.showModal = React.createRef()
this.state = {
snippets: [],
edit: null,
show: false,
sid: '',
}
}
The function looks like this
handleModal = snippet => {
console.log(snippet) //pastes output found in the snippet array above
console.log(this.state) //{snippets: Array(3), edit: null, show: false, sid: ""}
this.setState({ edit: this.snippet })
console.log(this.state)//{snippets: Array(3), edit: null, show: false, sid: ""}
this.setState({ sid: snippet._id })
this.showModal.current.showModal()
}
I know I have two setStates. I am trying to isolate the problem.
edit: null in the state should be becoming edit: Array(1) but setState seems to be failing.

There is two problems:
this.snippet
You should update your state using snippet instead of this.snippet
this.setState({ edit: snippet })
this.setState is asynchronous
The method this.setState() is asynchronous. Then, if you log right after it, the state problably won't be updated already. If you want to see the state after the update, passes a callback to the method, like below:
this.setState({ edit: snippet }, () => console.log(this.state))

You should use snippet instead this.snippet.

setState doesn't update state immediately. It can take some time. If you want to do some actions only after state has been updated, put you code in setState callback.
this.setState({...newState}, () => {
// actions after state update.
})

this.snippet is noting, you should use
this.setState({ edit: snippet })
Hope it helps

setState is asynchronous and the state is not updated immediately. Try to use the second parameter of setState - callback function
this.setState({ sid: snippet._id }, () => this.showModal.current.showModal())

Related

How to use spread operator in setstate react class component

I am developing a component where I will get the data from a call back function. Initially the state of the component will be empty [], later once the callback function is called I need to update the values into the state. At a time I'll recive only one array, meaning user can add one item at a time that item will consists of nested objects and array values. I have added the logic for the same to handle the scenario, but when I am testing in jest when I am trying to add another set of item from mock meaning the user can select next item when the done with selecting and submitting the first item at that time my logic is getting failed, I am not getting where I went wrong, could any one help me to resolve this issue, thanks in advance! I have added the mock data structure and logic and jest test below.
Mock:
const items = {
itemList: {
itemOne: [{
id: "01",
category: "It-A",
isCreated:"true"
}],
itemDesc:[{
id:"01",
type:"A-1",
isCreated:"true"
}]
}
ItemID:'123'
}
Code:
class ItemComp extends React.Component{
this.state = {
processingItems:[]
onAddItemHandle = (processingItem) => {
this.setState(prevState => ({
processingItems: [...prevState.processingItems, processingItem]
}))
}
JEST:
describe('handleonAddItem', () => {
it('should allow to add multiple items based on prevState', () => {
const compView = mountWithIntl(
<compView
itemId={12}
/>
}
const instance = compView.find(compViewComponent).instance();
instance.onAddItemHandle(items) // when I am giving only one instance my logic is working
instance.onAddItemHandle(items) //when I am giving it for second time it's failing I am getting error like expected - 0 , received +18 I want to update the items here when user clicks for second time but it is failing.
expect(instance.state.processingItems).toEqual([items])
Missing a ',' before the ItemID is the only issue I faced while reproducing.- https://codesandbox.io/s/intelligent-chaplygin-0ot56e?file=/src/App.js
const items = {
itemList: {
itemOne: [{
id: "01",
category: "It-A",
isCreated:"true"
}],
itemDesc:[{
id:"01",
type:"A-1",
isCreated:"true"
}]
},
ItemID:'123'
}

data stored in array but for specific elements it is showing undefined in react js

componentDidMount() {
axios.get('http://localhost:4000/info/admincontact').then(res => {
this.setState({
blogid: res.data
});
console.log(this.state.blogid)
console.log(this.state.blogid.city);
})
###CONSOLE
Array(2)0: city: "Salem"email: "sachinvijay#gmail.com"map: "https://www.google.com/maps/place/ITPL/#12.9859537,77.7322496,13z/data=!4m8!1m2!2m1!1sitpl!3m4!1s0x3bae11e4fb1b21a5:0x6018797bb4fac8b2!8m2!3d12.9874525!4d77.7370219"phone: "9600337763"state: "Tamil Nadu"__v: 0_id: "5f45444391b5a456bc90d7f7"proto: Object1: city: "Salem"email: "sachinvijay#gmail.com"mapl: "https://www.google.com/maps/place/ITPL/#12.9859537,77.7322496,13z/data=!4m8!1m2!2m1!1sitpl!3m4!1s0x3bae11e4fb1b21a5:0x6018797bb4fac8b2!8m2!3d12.9874525!4d77.7370219"phone: "9600337763"state: "Tamil Nadu"v: 0_id: "5f45497b9120014680289a99"proto: Objectlength: 2__proto: Array(0)
Contact.js:49 undefined
React setState is not a synchronous function, so you'd better write like this.
this.setState({blogid: res.data}, () => {
console.log(this.state.blogid)
console.log(this.state.blogid.city);
});
That is an array in the blogid, so you need to either iterate or you can use the particular index of it:
this.setState({blogid: res.data}, () => {
console.log(this.state.blogid[0].city);
});
and yes you have to log it in the callback of the setState because it is async and it's value take a bit of time to update. Callback will ensure that it will only log when the setState operation is done/fulfilled.
or you can use forEach:
this.state.blogid.forEach(obj => console.log(obj.city));

Click on React list of elements and store it as a value in another list

I have this sample code which is a search input that filters and returns different colors in a list. When you click on one of the items the innerHTML from that list item then populates the value in the input field.
My question is when I click on one of the list items, I would want to get that specific list item and store it in another list. Much like a search history. Is this difficult to add to the current state of the search field? How would you suggest that I'd approach this?
Do i need to have some kind of onSubmit function to achieve this?
See this example:
https://codesandbox.io/s/autocomplete-6lkgu
Thanks beforehand,
Erik
You can create a state variable searchHistory, which can be array, and onClick of the item, you can do :
onClick = e => {
this.setState({
activeSuggestion: 0,
filteredSuggestions: [],
showSuggestions: false,
userInput: e.currentTarget.innerText,
searchHistory: [...this.state.searchHistory, e.currentTarget.innerText],
});
};
I'm assuming you want all of the search items to be unique? You can use a set for that. Here is the code below for your AutoComplete component.
constructor
constructor(props) {
super(props);
this.state = {
activeSuggestion: 0,
filteredSuggestions: [],
showSuggestions: false,
userInput: "",
searchHistory: new Set([])
};
}
onClick
onClick = e => {
const { searchHistory } = this.state;
searchHistory.add(e.currentTarget.innerText);
this.setState(
{
activeSuggestion: 0,
filteredSuggestions: [],
showSuggestions: false,
userInput: e.currentTarget.innerText,
searchHistory: [
...searchHistory,
searchHistory
]
},
() => {
console.log("[search history]", this.state.searchHistory);
}
);
};
You can simply create an array in your state and push each option into it inside your onClick handler. I made a simple example here that just logs the history when it changes. You can then use/display it any way you'd like.
Note that you'd also need to add similar functionality inside of your onKeyDown handler, as it appears you're also triggering a change with the enter key. I updated the fork to include this.

React-native: undefined is not an object

Probably it's a newbie question... I get a json response with an object from a fetch() running into a function on componentDidMount(). The result is saved into a state
data:
{
id: 1,
name: 'joseph',
tickets:
[
{id: 334, descripton: 'example1'},
{id: 768, descripton: 'example2'},
],
}
I need to list this array "tickets" in render ().
componentDidMount(){
this.getFetch(...);
}
showTickets(WTickets){
console.log(WTickets);
WTickets.map((item)=>{
return (item.id)
})
}
render(){
return(
<View>
{this.showTickets(this.state.data.tickets)}
</View>
)
}
But the "first return" is "undefined", generating error and then the state changes to the correct result. The fetch is running with async await, but still show the "undefined" first.
The console.log show 2 results: one "undefined" and another with the result.
Any good soul to help me, please?
It's because at the start this.state.data.tickets is undefined and you are calling it in the render function which is not gonna wait until this.getFetch() finishes. So.. you can do a conditional rendering to check if this.state.data.tickets exist in rendering
replace {this.showTickets(this.state.data.tickets)}
with {this.state.data!==undefined? this.showTickets(this.state.data.tickets) : null}
What we are doing here is first checking if this.state.data.tickets is not undefined. While it is undefined (at the start) we return null, when it stops being undefined we call this.showTickets.
You can even initialize this.state.data as an empty array, and you can delete the part when we check if it's undefined since an empty array will return false
constructor() {
super();
this.state = {
data: []
};
}
....
....
//in render function
{this.state.data? this.showTickets(this.state.data.tickets) : null}
...

How to update the state varibles all at once in react JS

My requirement is from page1 to page2(on form submission) user navigates on some actions. if User navigates to page2 to page1(backward) then all the form fields in page1 should be filled. So i have stored all the data in session but on backward navigation not able to assign all the state values from session.
page1 componentmount code:
componentWillMount() {
if (sessionStorage.getItem("regData")) {
let formdata = sessionStorage.getItem("regData");
JSON.parse(formdata, (key, value) => {
this.setState({key:value});});
}
}
//state variables
this.state = {
username: "",
password: "",
email: "",
name: "",
mobile: "",
city: "",
redirectToReferrer: false,
error: {
email_error: "",
password_error: "",
name_error: "",
username_error: "",
mobile_error: "",
showError: false,
errorMessage: engTranslations.global.faild
},
value: "",
suggestions: [],
selectedCity: []
};
Let me know how to assign all state variables at once in component mount method. Thanks in advance.
You don't use the callback for JSON.parse (that's a "reviver" function which is to help with deserializing the data). Instead, take the result of JSON.parse and pass it into setState:
this.setState(JSON.parse(formdata));
More:
JSON.parse
setState
Important information for setting state when it's based on other state or props (yours isn't, so we can just pass an object into setState, but many state updates are based on state or props, and for those, you have to use the callback form of setState)
It's also worth noting that componentWillMount isn't the right lifecycle method to be doing this in. Since there's no asynchronous operation involved, the right place would be your constructor, and your constructor is the one place you can assign directly to this.state, so:
constructor() {
this.state = Object.assign(
{/*...default state here...*/},
JSON.parse(sessionStorage.getItem("regData")) // see note
);
}
(If there's no regData, getItem will return null, which will pass through JSON.parse [it gets converted to "null" and then back to null] and ignored by Object.assign, so we don't need to have the if statement.)
(If there were an asynchronous operation involved, it would be componentDidMount and you'd use setState instead..)
I'm not entirely sure if I got right the requirement but I guess what you need to do, assuming the session storage value contains all the fields you care of, is instead of doing this:
JSON.parse(formdata, (key, value) => {
this.setState({key:value});});
});
Do this:
this.setState(JSON.parse(formdata));
Also as side notes, take into account that using componentWillMount for setting state is considered an anti-pattern(https://vasanthk.gitbooks.io/react-bits/anti-patterns/04.setState-in-componentWillMount.html).
I also think it's not a great idea to store a user password in the session storage.
You can try this, setting state once:-
if (sessionStorage.getItem("regData")) {
let formdata = sessionStorage.getItem("regData");
let obj = {};
JSON.parse(formdata, (key, value) => {
obj.key = value;
});
let newState= {...this.state, ...obj};
this.setState(newState);
}

Categories