Trouble with printing parent state from child (ReactJS) - javascript

I'm attempting to use a parent to control an editor component and a save component. The parent component has these functions:
constructor() {
super();
this.state = {
code: "",
current_program: 'new'
}
}
updateCode(event) {
this.setState({
code: event
});
}
save() {
console.log(this.state.code);
}
In it's render component, I have this:
<IDE code={this.state.code} updateCode={this.updateCode}/>
<Browser save={this.save.bind(this)} programs={this.props.programs} />
The IDE successfully calls update, and when I log the output from the updateCode function in the parent, it works properly. But... In my browser component, I have the following:
<Button className="width-30" bsStyle="primary" onClick={() => this.props.save()}>
<Glyphicon glyph="save" /> Save
</Button>
On click, it prints "", does this have to do with the fact that I bound "this" before the code in the parent state was updated? Is it just printing the old state? How can I make it update? Thanks.
Edit: I'm calling this from the IDE component: onChange={this.props.updateCode.bind(this)}

Solved it: I was binding the state of the CHILD in the IDE component, which meant that was what was updating, changing the implementation of the IDE component in the parent to the following: <IDE code={this.state.code} updateCode={this.updateCode.bind(this)}/>, allowed the states to update properly

Related

What is the best approach in React Application for the case without using Redux?

I want to submit data via an api on a click of button in the parent Component, on collecting data from multiple sub components in different hierarchy.
I have a discussion on the case with one of my colleague and he suggest me to have a method in a parent comp. and share that (as a handler) to child(s) in prop and then to further to its child(s), keeping child comp. as stateless comp. Such that when user inputs the data it will store into a state variable (in parent comp.) via that handler method. For e.g:
class Parent extends Component {
constructor(props){
this.state.data = {}
}
updateHndler(key, value){
this.setState({
data : {...this.setState.data, key: value}
})
}
}
const ChildA = props => {
onComplete(e){
this.props.updateHndler('text', e.target.value)
}
return (<div>
...
..
<input .. onChange={()=>{onComplete(e)}}
</div>)
}
As each sub-component setting the value in parent state variable for each value it is getting update and its render() method keeps trigger, considering the rendering cost for all the sub-components on each change is the above approach is even good enough?
Thanks

React.js: Changing the child state also changes its proptype passed by parent

The issue I'm having is that when I change the state of the child component, the state of the parent component is also changed. I'm passing the prop to the child as so:
{this.state.users.map((user, index) => (
<UserRow
key={user.id}
user={user}
removeUser={this.removeUser}
getUserInfo={this.getUserInfo}
finishFunc={this.finishChanges}
/>
))}
And then assigning that prop to the a state of the child like this:
state = {rowUser: this.props.user};
However, when I update this child state, it seems that the parent state updates without re-rendering.
I'm displaying the information through the prop, not the state
<div>{this.props.user.rules.length !== 0 ? this.props.user.rules.length : null}</div>
You can see the initial display here
When I click on the "Add" or "Remove" buttons, the state and display changes. You can see the change in the image here
I'm confused on how changing the child's state updates the parent's state without me specifically passing the data back to the parent. I also don't see how it is changing the display since I'm using the prop, not the state.
You can see all of my code in a sandbox here. You can see the permissions number change whenever you click the "add new permission" and "remove permission" buttons, which I don't want to happen.
How can I avoid having the parent state automatically change when I change the child state?
The Problem:
The issue lied in how I was changing the child's state state = {rowUser: this.props.user}. I was mutating the data and resetting the state like this:
addPermission = () => {
const tempUser = { ...this.state.rowUser };
tempUser.permissions.push({
property: "select permission",
info: "type info..."
});
this.setState({ rowUser: tempUser });
console.log("[UserEditingRow.js] permission added");
};
The Solution:
Changing the data directly (i.e. tempUser.permissions.push({...});) was causing a problem. I used React immutability helper to help update the child's state more specifically. So, the new function would be:
addPermission = () => {
this.setState(
update(this.state, {
rowUser: {
permissions: {
$push: [{property: 'select rule', info: ''}],
},
},
}),
);
console.log('[UserEditingRow.js] rule added');
};
Setting the state with immutability helper allowed the child component's state to change without updating the parent component's state and solved my problem.
After reviewing your Sandbox, the issue is you are assigning props.user to state.rowUser and is creating a pointer to the parent components value. This means whenever you change this.state.rowUser it will actually be updating the reference to props.user.
Solution
Use the constructor() to bind the props.user to state.rowUser, this is the recommend approach, additionally use the spread operator to create a new instance of the object passed into the component, this will ensure no pointer is created.
https://reactjs.org/docs/react-component.html#constructor
constructor(props){
this.state = { rowUser: ...props.user }
}
Additionally
Looking quickly at the sandbox I also found a few other anti-patterns that will be causing you some issues.
You are updating state directly Line 76 to 84 in UserRow then calling setState, what you should be doing is using the spread operator to clone the object const tempUserRow = { ...this.state.userRow } then manipulating the object and set it in state via setState. This will ensure your shallow compare in shouldComponentUpdate passes and re-renders your component.
You are using shouldComponentUpdate which is good, but your not doing anything that React.PureComponent does for you out of the box, so you should change extends React.Component to be extends React.PureComponent
https://reactjs.org/docs/react-api.html#reactpurecomponent

React - Call setState in parent when child is called

I am building a blog in react where I get the data from a JSON-API and render it dynamically. I call setState in my app.js to get the data from JSON via axios and route with match.params to the post pages(childs). This works fine, BUT if I call the child (URL: ../blog/post1) in a seperate window it doesnt get rendered. It works if I hardcode the state.
So, I see the issue, what would be the best way to fix it?
Parent:
class App extends Component {
constructor(props) {
super();
this.state = {
posts: []
}
getPosts() {
axios.get('APIURL')
.then(res => {
let data = res.data.posts;
this.setState({
posts: data
})
})
}
componentDidMount = () => this.getPosts()
Child:
UPDATE - Found the error:
The component threw an error, because the props were empty. This was because the axios getData took some time.
Solution: I added a ternary operator to check if axios is done and displayed a css-spinner while loading. So no error in component.
Dynamic Routing works like a charme.
You can do this with binding. You would need to write a function like
setPosts(posts) {
this.setState({posts});
}
in the parent, and then in the parent, bind it in the constructor, like so.
this.setPosts = this.setPosts.bind(this);
When you do this, you're attaching setPosts to the scope of the parent, so that all mentions of this in the function refer to the parent's this instead of the child's this. Then all you need to do is pass the function down to the child in the render
<Child setPosts={this.setPosts} />
and access that method in the child with
this.props.setPosts( /*post array goes here */ );
This can apply to your getPosts method as well, binding it to the parent class and passing it down to the Child.
When you hit the /blog/post1 url, it renders only that page. The App component is not loaded. If you navigate from the App page, all the loading has been done and you have the post data in the state of the child component.
Please refer to this SO question. This should answer your question.

Props not being passed - React Native

I am using react-native-modalbox to show a modal and save data as a result.
When I save the data, my modal has access to the parent flat list so I can call a getData() function correctly, and the flat list reflects the latest update:
Modal - saveItem() is executed when a save button is pressed.
saveItem = async () => {
...
this.props.parentFlatList.getData();
this.props.progress.getData(); //This function call returns an error
this.closeModal();
};
Parent - the onPressAdd function opens the modalbox
constructor(props) {
super(props);
this.state = {
...
};
this.onPressAdd = this.onPressAdd.bind(this);
}
...
onPressAdd = () => {
this.refs.addFoodModal.showAddFoodModal();
}
<AddFoodModal ref={'addFoodModal'} parentFlatList={this} />
However, when I try to link the modal to another parent I receive this error:
Possible Unhandled Promise Rejection (id: 0):
TypeError: Cannot read property 'getData' of undefined
I simply want the modal to call the getData() functions in both parents so they both receive the updated data in their states.
Another Parent - this does not open the modal but I assumed it could pass itself down as props as the other parent does?
<AddFoodModal ref={'addFoodModal'} progress={this} />
Not sure where you get progress, if I understand correctly, you can pass progress to ur parent component as a props like <Parent progress={progress} />, then in ur child modal component you can do .
Also passing this down to child component is not a good practice, you should only pass what you actually need to ur child component to avoid unnecessary re-rendering, which will affect performance. In your code, you should pass getData down to the child component as a props, not this.

How can a parent alter props for a child component in react?

I'm new to react, and am a little confused about something. I've read many articles online claiming that a component cannot alter its own props, but that a parent can alter the props of its children. However, I have seen nothing that actually shows how to do this.
I would like to be able to do this:
class Parent extends Component {
constructor(props) {
super(props);
this.childProps = {name:'name',age:12,type:'child'};
this.changeChild = this.changeChild.bind(this);
}
changeChildName(newName) {
//change prop 'name' of child here;
}
render() {
return (
<Child {...this.childProps} />
);
}
}
However, I can't figure out how to do this at all - despite almost all the material that I've read on React saying that a parent can change its child's props. What I can get to work properly is as follows:
class Parent extends Component {
constructor(props) {
super(props);
this.state = {name:'name',age:12,type:'child'};
this.changeChild = this.changeChild.bind(this);
}
changeChildName(newName) {
this.setState({name: newName});
}
render() {
return (
<Child {...this.state} />
);
}
}
It seems a bit overkill to me that the Parent needs to re-render when the only thing that I want to re-render is the child view. How can I change the props of the <Child> component from the <Parent> component?
Secondary Questions
1) Is it possible to change the state of a component without having the component re-render?
2) Is there an onStateChange type event that we can tap into, or do we solely have componentWillReceiveProps?
1) Is it possible to change the state of a component without having the component re-render?
No, and it makes very little sense.
2) Is there an onStateChange type event that we can tap into, or do we solely have componentWillReceiveProps?
There is no, use componentWillReceiveProps.
It seems a bit overkill to me that the Parent needs to re-render when the only thing that I want to re-render is the child view.
Actually this is where you trick yourself: the parent IS changed, since what it returns is changed. The whole tree is being re-rendered from the root.
I think you misunderstand. It is very simple for a parent to change the child props... Just change the props that get rendered. Now, what if we want to do some changes in child without rerendering the parent. This is also pretty simple.
Attach a ref to your child and work with it. Now, the child cannot change its own props, so anything you mess with within the child will not be a prop. Props are passed down from the parent every time the parent rerenders.
So,
class Parent extends Component {
constructor(props) {
super(props);
this.childProps = {name:'name',age:12,type:'child'};
}
doSomethingOnChild() {
this.refs.child.callFunctionOfChildThatWillMessWithItself();
}
render() {
return (
<Child {...this.childProps} ref="child" />
);
}
}

Categories

Resources