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

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);
}

Related

Firebase Function With Converter Wipes Document Reference on Set()

I have a scheduled function that resets an integer value back to zero in my firestore. The problem that I'm running into is that, while the merge-set succeeds (for the specified properties), it somehow resets my Organization document reference to null.
So far I've tried the following
Not using a converter along with the Update() function (instead of Set()). While this works, it is untyped, and I have to get rid of the converter which encapsulates the moment() to Date conversion.
Using Set() and simply pass the entire object.
user.reference?.withConverter(userConverter).set(user)
This is also working but it overrides the entire user object and can lead to concurrency issues in case a user also updates his object while the timed function is running.
I'm looking for a solution that allows me to use the converter class along with a merge Set().
The User interface looks like this
export interface User extends Document {
email?: string
name?: string
organization?: Organization | null
numberOfForwards?: number
lastForwardReset?: moment.Moment
}
with its converter like so
export class UserConverter implements firestore.FirestoreDataConverter<User> {
toFirestore(user: User): firestore.DocumentData {
return {
email: user.email,
name: user.name,
organization: user.organization ? user.organization.reference : null,
number_of_forwards: user.numberOfForwards,
last_forward_reset: user.lastForwardReset?.toDate()
}
}
fromFirestore(snapshot: firestore.QueryDocumentSnapshot): User {
const data = snapshot.data()!
return {
reference: snapshot.ref,
email: data.email,
name: data.name,
organization: data.organization ? { reference: data.organization } : null,
numberOfForwards: data.number_of_forwards,
lastForwardReset: moment(data.last_forward_reset.toDate())
}
}
}
export const resetNumberOfForwards = functions.pubsub
.schedule('every 15 minutes')
.onRun(async () => {
const reset = (user: User) => {
console.log(`Resetting ${user.email} from [${user.numberOfForwards}] to [0]`)
// Claim user reference
user.reference
?.withConverter(userConverter)
.set({ numberOfForwards: 0, lastForwardReset: Moment() }, { merge: true })
}
for the partial set to work, I've included the following snippet on top of my file
firebase.firestore().settings({
ignoreUndefinedProperties: true
})
I think there are two issues going on here. For a partial set() you should use the merge option or else it will overwrite the document.
ref.set(data, {merge: true})
In addition, in your toFirestore method, either set the organization field as undefined and let the ignoreUndefinedProperties: true setting remove it, or don't include it at all if organization was not given. Something like this
toFirestore((numberOfForwards, lastForwardReset, ...user): User): firestore.DocumentData {
if (user.organization) {
user.organization = user.organization.reference;
}
return {
...user,
number_of_forwards: numberOfForwards,
last_forward_reset: lastForwardReset?.toDate()
}
}
I took out the numberOfForwards and lastForwardReset fields from the user object here and use the spread operator to copy over the remaining fields to the return value, but you could also save a temporary object, modify it, and return that.
PS: I know this is old, but it came up in my search so thought I might add an answer still.

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.

this.setState() not updating state with passed array

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())

React: Why is my child component updating the state of its parent?

I'm just making a simple form component that reads in the user information and displays it in the form as values. After being edited the change can be saved or canceled. However the local state updates it's parent state which has got me scratching my head.
I have a parent component with a user info object inside its state object:
usrInfo: {
userName: {name: 'usrname', title: "User Name", value: 'state.user'},
firstName: {name: 'fname', title: "First Name", value: "asdf"},
lastName: {name: 'lname', title: "Last Name", value: "asdf"},
title: {name: 'title', title: "Title", value: "asdf" },
email: {name: 'email',title: "E-mail",value: "asdf#asdf.com"}
},
my child component displays this state no problem. An edit button is clicked in the child and calls a function also in the child to set the userCashe child state to it's parrent user state:
casheState() {;
this.setState((prevState, props) => {
return {userInfoCashe: props.user };
})
}
then a form populates with the userInfoCash as values and when edited it updates the child state:
changeHandler = (event) => {
let usrCopy = {...this.state.userInfoCashe};
usrCopy[event.target.id].value = event.target.value;
this.setState({userInfoCashe: usrCopy});
console.log(this.state.userInfoCashe[event.target.id].value)
console.log(this.props.user[event.target.id].value)
// ^^ these are the same, why?
};
this function mutates it's parent user state. HOW AND WHY IS THIS HAPPENING?! I thought that react was built on one-way data binding.
Thanks!
this.setState doesn't update the state immediately. Its async. So if your console.log shows the same state before and after, then its because of this only.
You can try doing something like this :
this.setState({userInfoCashe: usrCopy}, ()=> {console.log(this.state.userInfoCashe);})
to actually see if your state got mutated. Hope this helps.
first I would like to suggest to use lodash npm it will support javascript functions
reference link : https://www.npmjs.com/package/lodash
install lodash : npm install --save lodash
after installing it import it in your file wherever you want to use
import _ from "lodash";
changeHandler = (event) => {
let usrCopy = _.cloneDeep(...this.state.userInfoCashe);
usrCopy[event.target.id].value = event.target.value;
this.setState({userInfoCashe: usrCopy});
console.log(this.state.userInfoCashe[event.target.id].value)
console.log(this.props.user[event.target.id].value)
// ^^ these are the same, why?
};
please try this, it will not update you original state it will update only the where took the copy of state.

Javascript. adding items to an array updates all items

This question is somewhat related to this issue I had earlier today:
Adding items to an array in javascript
It works to add items to my array now, but it seems that when I update the array all items will be the same even though the object passed into the method is different everytime
My method looks like this:
addShoe(shoe) {
console.log("Adding new shoe to collection: ");
console.log(shoe);
this.setState(
{
shoes: [...this.state.shoes, shoe]
},
function() {
console.log("Shoe collection:");
console.log(this.state.shoes);
}
);
}
So after one run, this is what the console in Chrome looks like. Which seems to be right:
When I try to add one more to the collection, this is what happens:
Now my collection contains two items which is correct, but it seems like all items in the collection has the same data?
What am I doing wrong here?
EDIT
In another React component I have the following state:
this.state = {
shoe: {
selectedBrand: "",
selectedEU: "",
selectedUS: "",
selectedUK: "",
selectedFraction: ""
}
};
Once a field is updated with a new value, the following method will be triggered:
updateSelectedValues(property, event) {
const shoe = this.state.shoe;
shoe[property] = event.value;
this.setState({ shoe: shoe });
}
When a button in this modal window is closed, the this.state.shoe will be pass as a param to method in the "parent" component.

Categories