Accessing state in a parent component from a child component - javascript

I'm currently accessing the state of a parent component in a child component through the use of callbacks, but I'm not quite sure how it works:
export default function parentComponent({ type }) {
const
[inEditMode, setInEditMode] = useState(false)
return (
<>
<div className={containerCssClasses}>
<childComponent onCancel={() => { setInEditMode(false); }} type={type} />
here, the parent component is calling the child component and passing an anonymous function to the component for it to call back.
In the child component:
function childComponent({onCancel}) {
return (
<div>
<Button onClick={() => onCancel()} variant="link" >
</div?
);
}
I am a bit confused how this is able to work properly if the child component doesn't have access to setInEditMode(). I understand that when passing a function to another function (in this case, a component), it is passed by reference. But does that necessarily mean all of the variables within that function are passed by reference as well? I am new to react (and javascript), but when I looked this concept up I couldn't find a good explanation.

Your implementation is the right way to go. More than callbacks these functions you pass down to child components are call props and I invite you to read the documentation about them.
https://reactjs.org/docs/components-and-props.html
You can also find very detailed articles about it:
https://itnext.io/what-is-props-and-how-to-use-it-in-react-da307f500da0
Whatever you'll pass from the parent to the child can be used in the child component as if it was called in the parent one.
In your case the child is indeed gonna trigger setInEditMode(false)
As you're not passing any variable to it you can actually call it as such:
<Button onClick={onCancel} variant="link" >

The parent component's state you pass to child component as props is not updated as parent's state is changed. so, you need a callback function to access parent's current state at any given time. Also, if you have dependent state value in parent and child component or rather multiple components, you can move it to reducer if you're using redux for state management.

Related

How this.setState() (react.Component method) has access of his child's class state?

Actually, I am learning to React JS, But I have confusion that how can a parent's class method has access to his child's class state. I searched a lot on this topic but in object-oriented programming parent class hasn't access to his child's properties (state). But in react js how setState() has to access to his child's class properties (state). It can be a stupid question please tell me how it happens?
Don't worry, this is a great question.
Props are passed by reference!
Here are some great answers from a StackOverflow post that answer your question with more specificity.
In reactjs, are props pass by value or pass by reference?
I think some potential nuance I can offer is that React emphasizes composition over inheritance. You should think of it as your parent component being composed of the child components. While the child components are given access to the parent state, they aren't inheriting it.
Understanding the Parent Child structure through the Component API may help, should you want to clear any confusion. Since you have mentioned Class Components, let me illustrate an example with the same.
You may send props from a Parent to a child through defining a property within the JSX insertion -
<Child uniqueProp="Text sent to Child" />
Conversely, it is tricky to access Child data in a Parent component. You'll have to follow these steps -
Define a callback function in the Parent Component to get data from the Child Component.
Pass the callback function in the Parent Component as a prop to the Child Component.
The Child Component calls the Parent callback function using props.
Example -
Parent Component
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
text: "Original Text"
}
}
handleCallback = (value) => {
this.setState({ text: value })
}
render() {
return (
<div>
<Child changeText={this.handleCallback} />
{this.state.text}
</div>
)
}
}
Child Component
class Child extends React.Component {
render() {
return (
<div>
<button onClick={() => this.props.changeText("Data from child")}>
Button
</button>
</div>
)
}
}
The reason it is set up that way is because props are passed by reference. Should you choose to change the parent's state traditionally state = {"something"} instead of setState({"something"}), the object will be modified in the child component as well but there will not be a re-render cycle. The user won't see UI changes until a manual refresh.
And when you use setState to change the parent's state (standard practice), the re-render cycle will be triggered as expected and the Child Component will reflect the UI changes.
Note: Be warned, the use of functional components is almost defacto now. Makes life easy with integrations of Hooks and data stores such as Redux in my opinion.

React - How can I set my parent state using child component, using function structures (not classes)

I'm trying to change the state of parent component using a button in my child component by passing a function "setName" to the child. I read a lot of examples but they seemed to all use classes, where I'm trying to use functions since the React doc on hooks seems to promote functions over class.
In my parent, I have
let [Name, setName] = useState("John Doe");
And I pass the setName method by using JSX in my return statement:
<Child childSetName=({setName})/>
In my child component I have
export const Child = ({childSetName}) => {
return(
<a onDoubleClick={()=>{childSetName("Mary Ann")}}>
Click me
</a>
)}
I got a typeError: childSetNameis not a function.
I also read this popular question, but it uses class, not functions as all the hooks example from React docs are.
How can I set my parent state using child component, both retain their function structures not classes.
I think you have wrong syntax here
<Child childSetName=({setName})/>
Should change like this
<Child childSetName={setName}/>
and
export const Child = ({childSetName}) => {
return(
<a onClick={()=>{childSetName("Mary Ann")}}>
Click me
</a>
)}

How a component pass a prop to another component?

Newbie here, I am studying the documentation of react and in React Context API, I couldn't understand something, I won't understand the rest of the subject if I don't understand it. Can anyone help me what does it mean through using an example?
The Toolbar component must take an extra "theme" prop
and pass it to the ThemedButton. This can become painful
if every single button in the app needs to know the theme
because it would have to be passed through all components.
class App extends React.Component {
render() {
return <Toolbar theme="dark" />;
}
}
function Toolbar(props) {
// The Toolbar component must take an extra "theme" prop
// and pass it to the ThemedButton. This can become painful
// if every single button in the app needs to know the theme
// because it would have to be passed through all components.
return (
<div>
<ThemedButton theme={props.theme} />
</div>
);
}
class ThemedButton extends React.Component {
render() {
return <Button theme={this.props.theme} />;
}
}
The Toolbar component must take an extra "theme" prop
this can be like <Toolbar theme="dark">
and pass it to the ThemedButton
how Toolbar component pass this prop to ThemedButton? and kindly clarify the rest of the comment as well.
Thank you for any help? You are kind
In your Toolbar component, it takes a parameter props, props is whatever properties have been passed to it when calling it, as in <Toolbar param1="someString" param2={someVariable}>, in this case the props value in Toolbar will be an object with the data you passed as key=value like for example: {param1: "someString", param2: content_of_someVariable}
And if you don't actually use those props (properties)/parameters in Toolbar, but rather in a subcomponent, then you have to pass them again to another level, like in <ThemedButton theme={props.theme} />, then ThemedButton itself finally passes the value to the component that actually makes use of, which is in your case: <Button theme={this.props.theme} />;.
So you had to pass the theme across multiple components, which don't use it or care at all about it, just to get it through to the final Button component.
(answer ends here, below is my effort to explain context API in an easy way)
To avoid that annoying level to level to another..., you can use the context API. Because it is really incontinent to pass a value across 3-4+ levels/components every time you want to use it in the last one in the chain.
Think about the context like a variable defined and exported on a root level and holds some data (like the user login status for example, or the theme infomation), and whenever you require that data, you import it and use it directly. You use the Provider property of the context you define (MyContext.Provider) to assign the data to it, and you use the Consumer property (MyContext.Consumer) to consume/access that data you assigned in the provider.
The beauty of the context consumer, is that whenever the data is updated in the provider, the consumer immediately gets the new data and triggers a re-render with the new data.
I hope I explained it in a simple and clear way. Write a comment with any questions or unclear parts and I can try my best to improve the answer.
Best of luck!
Props are properties that help define the way your JSX appears on the page.
When you use a component that you have created, you can pass it props like below:
<MyComponent myProp={myPropValue} />
You can continue to pass props down through the component hierarchy as well. So say you have a component tree like below:
MyComponent
--MySubComponent
----MySubSubComponent
You can pass props from MyComponent to MySubSubComponent like so:
<MyComponent myProps={MyProps} />
<MySubComponent mySubProps={props.myProps} /> //Props are the value you gave in the parent component
<MySubSubComponent mySubSubProps={props.mySubProps} />
Whatever title you give the props when declaring the component in JSX is the title you will call to get the value of the prop like props.myProps

calling and pass arguments to children component's function from parent component in reactjs

I found out that my argument passed from parent to child component through refs is either undefined or an actual event instead of what I passed in.
What is the right way to actually pass argument from parent to child component's function? I am using refs but seems its not really passing anything in term of function argument.
I show the case here:
https://codepen.io/adamchenwei/pen/gLMaPo/?editors=0010
Basically if you see the line where I wrote console.log('child argument should be string'); it should be an argument actually passed instead of an event.
I wonder if I did things wrong.
For this scenario, I NEED to evaluate event in parent component and decide how I want to run the child function, with specific arguments passed in accordingly.
There are only two way to access parent props in child component. you can achieve this in following two ways:-
Pass values into child component from parent via props
Set values in state and dispatch action from child to fetch values from state.
Instead of using ref, I would use state and props:
parentDoThing(event) {
const lifeTerms = {
heaven: 'yeeeeey',
hell: 'nooooooo',
}
if(event) {
this.setState({ lifeTerms: heaven });
} else {
this.setState({ lifeTerms: hell});
}
}
render() {
return (
<section className="parent-comp"
onClick={this.parentDoThings}>
<ChildComponent
lifeTerms={this.state.lifeTerms}/>
<h1> Whats in App {this.state.text}</h1>
</section>
);}
And on child component:
render() {
return (
<section className="child">
<section>this life is {this.state.life} </section>
<button onClick={this.doChildThings.bind(this, this.props.lifeTerms)}> Button </button>
</section>
);
Resolved my issue by using componentWillUpdate in combine with using parent level state passed into child as props to trigger event inside the child, because I do not want any trigger inside the child to trigger the change provided by the parent.

Why can't I update props in react.js?

Why do we have both state and props? Why don't we just have one source of data? I'd like to update a component's props and have it re-render itself and all of its children. Seems simple but I can't figure out how to let a component update its own or its parent's props.
Thanks for any help.
The React philosophy is that props should be immutable and top-down. This means that a parent can send whatever prop values it likes to a child, but the child cannot modify its own props. What you do is react to the incoming props and then, if you want to, modify your child's state based on incoming props.
So you don't ever update your own props, or a parent's props. Ever. You only ever update your own state, and react to prop values you are given by parent.
If you want to have an action occur on a child which modifies something on the state, then what you do is pass a callback to the child which it can execute upon the given action. This callback can then modify the parent's state, which in turns can then send different props to the child on re-render.
To answer the question of why
In React, props flow downward, from parent to child.
This means that when we call ReactDOM.render, React can render the root node, pass down any props, and then forget about that node. It's done with. It's already rendered.
This happens at each component, we render it, then move on down the tree, depth-first.
If a component could mutate its props, we would be changing an object that is accessible to the parent node, even after the parent node had already rendered. This could cause all sorts of strange behaviour, for example, a user.name might have one value in one part of the app, and a different value in a different part, and it might update itself the next time a render is triggered.
To give a fictional example:
// App renders a user.name and a profile
const App = (props) =>
React.createElement('div', null, [
props.user.name,
React.createElement(Profile, props)
])
// Profile changes the user.name and renders it
// Now App has the wrong DOM.
const Profile = ({user}) => {
user.name = "Voldemort" // Uh oh!
return React.createElement('div', null, user.name);
}
// Render the App and give it props
ReactDOM.render(
React.createElement(App, {user: {name: "Hermione"}}),
document.getElementById('app'))
);
We render app. It outputs "Hermione" to the Shadow DOM. We render the Profile, it outputs "Voldemort". The App is now wrong. It should say "Voldemort" because user.name is "Voldemort", but we already output "Hermione", and it's too late to change it.
The value will be different in different parts of the app.
Modifying Props would be two-way-binding
Mutating props would be a form of two-way binding. We would be modifying values that might be relied on by another component higher up the tree.
Angular 1 had this, you could change any data anytime from wherever you were. In order to work, it needed a cyclical $digest. Basically, it would loop around and around, re-rendering the DOM, until all the data had finished propagating. This was part of the reason why Angular 1 was so slow.
In React, state and props serve different goals: state allows a component to maintain some changing values, while props are the mecanism to propagate those values to children.
Children are not allowed to alter by themselves the values they get via props just because React designers find it easier to maintain an application built this way. Their point is that when only one component is allowed to update some piece of state, it is easier to discover who altered it, and find the root of bugs.
the Component itself changes its state, and changes not its own, but the children's props.
<Parent>
<Child name={ this.state.childName } />
</Parent>
Parent can change its own state and change the child name, but it will change the props for his children.
edit1:
for calling events from the child to its parent, you should pass in the child an event handler like so:
var Child = React.createClass({
render: function() {
return (<button onClick={ this.props.onClick }>Hey</button>);
}
});
var Parent = React.createClass({
onChildClick: console.log.bind(console), // will print the event..
render: function() {
return (<Child onClick={ this.onChildClick } />);
}
});
React.renderComponent(<Parent />, document.body);
in this code, when you'll click on the Child's button, it will pass the event to its parent.
the purpose of passing the events is decoupling the components. maybe in your app you need this specific action, but in another app you'll have, you'll use it differently.
My solution was fairly different but some people might run into it. On the Chrome Dev tools, it kept saying that my props were read-only and when I tried passing them down even further, I would get an error. Now, the reason why is because I wasn't invoking a render() method. I was instead calling my component like this:
const Navigation = () =>{
return (
<div className="left-navigation">
<ul>
<Link to='/dashboard'><li>Home</li></Link>
<Link to='/create-seedz'><li>Create Seedz</li></Link>
<Link to='/create-promotion'><li>Create Promotion</li></Link>
<Link to='/setting'><li>Setting</li></Link>
<SignOutButton />
</ul>
</div>
);
}
I added a render method and it solved my issue of being able to pass props down:
class Navigation extends Component{
render(){
return (
<div className="left-navigation">
<ul>
<Link to='/dashboard'><li>Home</li></Link>
<Link to='/create-seedz'><li>Create Seedz</li></Link>
<Link to='/create-promotion'><li>Create Promotion</li></Link>
<Link to='/setting'><li>Setting</li></Link>
<SignOutButton user={this.props.user} signedOut={this.props.signedOut} authed={this.props.authed}/>
</ul>
</div>
);
}
}
Hopefully this helps someone.
Contrary to the answers provided here, you actually can update props directly, if you don't mind defying the pedantic circlejerk about "the React way." In React.js, find the following lines of code:
Object.freeze(element.props);
Object.freeze(element);
and comment them out. Voila, mutable props!

Categories