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

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.

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'
}

React, losing saved data in localStorage with useEffect after page refresh

This is probably a noob question, but I'm facing some troubles with the useEffect() hook. I have a Taking Notes App, and I want to make the data persist. I'm using 2 useEffects: one for when the page is refreshed/loaded by the first time, and other one for when I add a new note to my app.
I putted some logs to check what's happening:
const [notes, setNotes] = useState([
{
noteId: nanoid(),
text: 'This is my 1st note!',
date: '30/07/2022'
},
{
noteId: nanoid(),
text: 'This is my 2nd note!',
date: '30/07/2022'
}
])
// 1st time the app runs
useEffect(() => {
const savedNotes = JSON.parse(localStorage.getItem('react-notes'))
console.log('refresh page call:',savedNotes)
if(savedNotes) {
setNotes(savedNotes)
}
}, [])
//every time a new note is added
useEffect(() => {
localStorage.setItem('react-notes', JSON.stringify(notes));
console.log('new note call:', notes)
}, [notes])
The behaviour is a bit strange, because when the page is refreshed the new data is appearing inside the log, but then it disappears, maintaining only the hardcoded data:
It also makes more calls than I was expecting to. Any thoughts about what is going on here?
Issue
The problem is caused by the below useEffect and how you are initially setting the state:
useEffect(() => {
localStorage.setItem('react-notes', JSON.stringify(notes));
console.log('new note call:', notes)
}, [notes])
The above useEffect runs every time notes changes, but also on mount. And on mount the state is equal to that initial array given to useState. So the localStorage is set to that array.
Solution
A solution is to change how you are setting the state as below, so you pick what's in the localStroge if there is something, and otherwise use that initial array you have:
const [notes, setNotes] = useState(
!localStorage.getItem("react-notes")
? [
{
noteId: nanoid(),
text: "This is my 1st note!",
date: "30/07/2022",
},
{
noteId: nanoid(),
text: "This is my 2nd note!",
date: "30/07/2022",
},
]
: JSON.parse(localStorage.getItem("react-notes"))
);

Redux Form needs to display my filtered values in the browser

Using Redux Form I'm able to retrieve the values which I enter in username. I'm able to filter out the corresponding values, but I need to display my filtered values in the browser.
console.log("pilot.name--->", pilot.name);
Can you tell me how to do it? I provided my code snippet and sandbox below. My related code is in showResults.js: https://codesandbox.io/s/xl1r14w854.
var pilots = [
{
id: 2,
name: "Wedge Antilles",
faction: "Rebels"
},
{
id: 8,
name: "Ciena Ree",
faction: "Empire"
},
{
id: 40,
name: "Iden Versio",
faction: "Empire"
},
{
id: 66,
name: "Thane Kyrell",
faction: "Rebels"
}
];
var rebels = pilots.filter(function(pilot) {
// return pilot.faction === "Rebels";
// return pilot.faction === values.username;
if (pilot.faction === values.username) {
console.log("pilot.name--->", pilot.name);
}
});
I suggest to use a separate reducer for this. Imagine that you call that reducer PilotsReducer where you have your list of pilots in the state. That reducer is "listening" to a specific action like loginSubmitted that you trigger when you get the response from the server.
The payload of that action will be the username that you use to do the filter. In the reducer you can then do the filter and set a state property (e.g. rebels) to the result of the filter. Your component can then be attached to redux to pick the rebels property and it will re-render when that property changes.
This is a typical flow of react/redux, it is not specific to your example
On a side note you can improve it by using reselect so you would keep the entire list of pilots in the state, and filter them in a selector by passing the current username to it. Please check the docs and tutorials of redux and reselect to have a clear view of the entire workflow.

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.

Vuejs2: how to judge props update when using object as a prop?

Suppose I have an array feedsArray, the example value may look like this:
this.feedsArray = [
{
id: 1,
type: 'Comment',
value: 'How are you today ?'
},
{
id: 2,
type: 'Meet',
name: 'Daily sync up'
}
]
Suppose I have registered two components: Comment and Meet, Each component has a prop setting as the following:
props: {
feed: Object
}
and the main component has the following definition:
<component v-for="feed in feedsArray" :feed="feed" :key="feed.id" :is="feed.type"></component>
As you can see, it uses is property to select different component. My question is, how to detect feed object change in the child component ? Like when I set
this.feedsArray[0] = {
id: 1,
type: 'Comment',
value: 'I am not ok'
}
How can the Comment component detect the changes ? I tried to add a watcher definition in the child component like the following:
watch: {
feed: {
handler (val) {
console.log('this feed is changed')
},
deep: true
}
},
But it doesn't work here. Anyone know how to solve this ?
Do not assign directly to an array using index - use splice() instead, otherwise JavaScript can not detect that you have changed the array.
If you want to change only the value of an already existing key of an object - then simply update it e.g. this.feeds[0].value = 'I am not okay any more';
This works for existing keys only - otherwise you have to use this.$set(this.feeds[0], 'value', 'I am not okay any more');

Categories