What exactly is a "Prop" in React? - reactjs

I just started using React native on iOS to see how it feels like and I have a silly question..
I see everyone talking about "Props", whenever I read an article or a tutorial, the author uses this term a lot and it's the same in the code. For instance, in class declarations I often see constructors like :
class MyClass extends Component {
constructor(props) {
super(props);
this.state = { message: '' };
}
}
I can't find a clear explanation of what a prop is, could anyone enlighten me about that ?

props are the values that you pass down to your React component. So whenever you have a component you'll see something like this:
<MyComponent value1={value1} isTrue={true} />

In addition to the answer of Keith, below you can find the non-JSX version that uses a 'color' property.
// Output (JS):
var app = React.createElement(Nav, {color:"blue"});

React Props are read-only arguments passed from one React Component to another (down the component hierarchy).
Let's say you have two components - App and List. Then, inside App we have a list with information from developers. If we wanted to pass the array to the List component, we'd do something like this (<List list={developers} />)
import React from 'react';
const App = () => {
const developers= [
{
id: 1,
name: 'Randy'
},
{
id: 2,
name: 'Tiago Peres'
},
];
return (
<div>
<h1>Developers in StackOverflow</h1>
<List list={developers} />
</div>
);
};
const List = props =>
props.list.map(item => (
<div key={item.id}>
<span>{item.name}</span>
</div>
));
export default App;
This would be the result
Using this, we don't need to have too much information in the global scope.
If you want to know more about it, there's a great article from Robin Wieruch that i would suggest you to read.
To understand the specific case you mention, one must know some basics of React and also ECMAScript 6 (ES6). Based in content from W3Schools's React Tutorial,
A class created with a class inheritance inherits all the methods from another class.
To create a class inheritance we use the extends keyword.
You're creating a class named MyClass which will inherit the methods from the Component class.
The properties of a class are assigned inside a constructor() method
The constructor function is called automatically when the object is initialized.
The super() method refers to the parent class and so executes the parent component's constructor function.
If the component has a constructor function, the props should always be passed to the constructor and also to the React.Component via the super() method.
super(props) would call the Component constructor passing in props as the argument.
While most likely you could just use super(), using super(props) ensures this.props is set even before the constructor exits.
Even though in your case the state object has only message property, you can have as many as you like.
You can use this.state.message anywhere in the component.

Props : Props is nothing but property of component and react component is nothing but a JavaScript function. Props are immutable. You can pass props between components.You can pass props from parent component to child component directly. For passing from child to parent you need use concept of lifting up states.
class Parent extends React.Component{
render()
{`enter code here`
return(
<div>
<Child name = {"Sara"}/>
</div>
);
}
}
class Child extends React.Component{
{
render(){
return(
<div>
{this.props.name}
</div>
);
}
}

Related

Sharing state among subcomponents in React

I'm currently developing an app that uses React in some parts of its UI to implement some complex feature that are very interactive. Some of these parts of the app, that are in fact React components, have become very complex in size.
This has led me to organize the code by dividing them in multiple subcomponents (and those in their own subcomponents, and so forth). The main problem is that some of the grandchildren of the main component might have the need to modify the state of another grandchildren that they're not related to. To do so, I end up having to have the state in the common parent and passing a lot of event handlers (methods of the parent component) to their children, and those children would need to pass it to their grandchildren.
As you can imagine, this is becoming some kind of a mess.
// MyComponent.js
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
list: [1, 2, 3, 4],
selected: '',
}
this.add = this.add.bind(this)
this.handleChange = this.handleChange.bind(this)
}
add() {
const newNumber = this.state.list[this.state.list.length - 1] + 1,
list = [...this.state.list, newNumber]
this.setState({list})
}
handleChange({target}) {
this.setState({
selected: target.value,
})
}
render() {
return (
<>
<List items={this.state.list} selected={this.state.selected} />
<Button onClick={this.add} />
<input type="text" value={this.state.selected} onChange={this.handleChange} />
</>
)
}
}
// Button.js
class Button extends React.Component {
render() {
return (
<button onClick={this.props.onClick}>Click me!</button>
);
}
}
// List.js
class List extends React.Component {
constructor(props) {
super(props)
this.refs = props.items.map(_ => React.createRef())
}
render() {
return (
<ul>
{this.props.items.map((item, key) =>
(<li ref={this.ref[key]} key={key}>{item}</li>)
)}
</ul>
);
}
}
In the previous dummy code you can see how I need to define the add() method in the MyCompoent component so that an action that happens in the Button component can modify what is being shown in List. Even tho this might seem like the obvious way to do it, my component has a big component tree, and a lot of methods, and most of then are lost in the tree, passing from parent to child until it reaches the component that should be expected.
I have done some research on the internet and it turns out this is a very common problem. In most sites, using Redux or other state management library is recommended. However, all the tutorials and guides I've seen that implement Redux with React seem to assume you're only using React to build your app, in Single Page Application sort of way. This is not my case.
Is there any way to share the state of a component to avoid this kind of problem? Is there, maybe, a way to use Redux multiple times for multiple components in the same app, where one store saves only the state for MyComponent and can be accessed by either List or any of its possible children?
Redux doesn't require your entire site to be in React. It implements a higher-level component that you can use with any React components even if they are embedded in another site.
You can look at React hooks to solve similar problems. Specifically, check out useContext() and useState().
You've used a lifting state up pattern in react in your example.
It's quite common you good approach but when you app is growing you need to pass all bunch of props throu the tree of components. It's difficult to maintain.
In this case you need to check out redux with separated store or useContext() hook.

How does React's Component's constructor really work?

I appreciate it if anybody can explain why the following code works.
I created a NumberListBase React component. Then created another one, named NumberList and derived it from NumberListBase.
In the constructor of the two components I purposely don't pass 'props' argument to the parent class when calling super().
class NumberListBase extends React.Component {
constructor(props) {
super();
Log("NumberListBase.ctor: ", this.props, props);
}
}
class NumberList extends NumberListBase {
constructor(props) {
super();
Log("NumberList.ctor: ", this.props, props);
}
render() {
const numbers = this.props.numbers;
const listItems =
numbers.map((n) => <li key={`${n}`}>{n}</li>);
return (<ul>{listItems}</ul>);
}
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
I expected that render() will fail, because this.props would be undefined in it.
The log messages I put in the constructors shows clearly that the 'props' argument and 'this.props' are 'undefined' in NumberListBase constructors.
But, in complete amazement, the component rendered correctly and showed the numbers, meaning that the 'props' got to React.Component somehow and React.Component could put it inside 'this.props'!
This is a codepen I created for this question.
https://codepen.io/mansoor-omrani/pen/oKaMXV
I also created a jsfiddle snippet to test how constructors work in a class hierarchy.
https://jsfiddle.net/omrani/thnj2zu4/11/
I checked React.Component source code to see how this class is defined.
https://github.com/facebook/react/blob/master/packages/react/src/ReactBaseClasses.js
function Component(props, context, updater) {
this.props = props;
this.context = context;
// If a component has string refs, we will assign a different object later.
this.refs = emptyObject;
// We initialize the default updater but the real one gets injected by the
// renderer.
this.updater = updater || ReactNoopUpdateQueue;
}
React.Component is just a simple function. I'm still wondering how, where and when 'this.props' was set!
When you call super() without the props, you’ll still be able to access this.props in the render and other methods because React assigns props on the instance right after calling your constructor.
We pass props into the super method only when we need to use props inside the constructor.
The reason why they implemented this behaviour is unclear, probably for future compatibility reasons, as Robin Pokorny replied to the below question.
ref: What's the difference between "super()" and "super(props)" in React when using es6 classes?

Assigning props to variable in React class component

I have a simple react component. I would like to assign a variable inside the component method to props. I've tried this method works with functional components:
class Pets extends React.component {
constructor(props) {
super(props)
}
const {dog, cat, frog} = props
// the code on the line above does not work
render() {
return (
<div>
{dog.name}
{cat.name}
</div>
)
}
}
That will not work. props is a property on the instance of the component you created with <Pets /> JSX for example. It needs to be inside the instance methods of the class Pets. It will work if you do it in the render method by const {dog, cat, frog} = this.props. props in the class body as you have now in the question is not what them expected to be.
Inside the constructor function just const {dog, cat, frog} = props will work, because here props object is received as an argument. so this.props (after the super(props) line)andprops` is same object.
Generally you unpack props where you need them, for example you need those inside the render to create some output, so you unpacked it there. I tried to explain in this answer what props means in the class body, and instance methods body, how you can access the props in different methods etc. But as xadm said, unpacking them inside the constructor by thinking you will access them later in other methods will not work due to the scope boundary, unless you store them as a property to the instance again using this.dog = props.dog. But this is an horrible idea, never do this. Just unpack them when you need any property from props object in that place only.

How does one React component call a method in another React component?

My page contains two completely separate React components (different files, different classes, no parent-child relationship).
How can one component call an instance method in another component? The problem seems to be obtaining the instance of the target component.
EDIT: Both components share the same parent (i.e. they are rendered in the same render() method) but I still don't know how to pass the reference of the target component to the calling component.
The short answer is: they don't.
It's not clear what you're trying to accomplish, so I can't speak to the specifics of your case, but the way React components "communicate" with one another is via state and props. For example, consider a Page component that has two child components, CompA and CompB, rendered something like this:
<Page>
<CompA />
<CompB />
</Page>
If CompA needs to pass something to CompB, this is done through state on the Page component, with that state exposed as props on CompA and CompB, something like this:
class Page extends React.Component {
constructor(props) {
super(props);
this.state = {
sharedValue: 42,
};
}
onChangeSharedValue(newValue) {
this.setState({ sharedValue: newValue });
}
render() {
return (
<div>
<CompA
sharedValue={this.state.sharedValue}
onChange={this.onChangeSharedValue}
/>
<CompB
sharedValue={this.state.sharedValue}
onChange={this.onChangeSharedValue}
/>
</div>
);
}
}
If CompA needs to change the shared value, it calls the onChange handler, which will change the state on the Page component. That value will then be propagated down to the CompB component.
There is no direct communication between components like you're describing; it is all done via state and props.
"Props down, Events up."
If you provide us a specific example of what you're looking for, I can update this post with a more specific response.
But in general, there are a couple of strategies that you can take. Some of them are presented here.
The preferred approach is to simply move your calling method to the parent component. It's a common strategy in React.
If you're not able to, then the next step would be to write an event handler for the parent, and then pass this event down to the first child component.
Use this event to pass information up to the parent, so that when it gets triggered, data can be passed as props down to the second component.
I only recently started doing React development and I found a solution for this problem that suits me. Admittedly, I haven't seen it referenced anywhere and when I showed it to a colleague who's been doing React for years, he kinda furrowed his brow and felt that it wasn't "right", but he couldn't really articulate to me why it's "wrong". I'm sure I'll be shouted down for it here, but I thought I'd share anyway:
File #1: objects.js
let objects= {};
export default objects;
File #2: firstComponent.js
import React from 'react';
import objects from 'objects';
class FirstComponent extends React.Component {
constructor(props) {
super(props);
objects['FirstComponent'] = this; // store a reference to this component in 'objects'
}
doSomethingInFirstComponent() {
console.log('did something in first component');
}
render() {
return (<div></div>);
}
}
export default FirstComponent;
File #3: secondComponent.js
import React from 'react';
import objects from 'objects';
class SecondComponent extends React.Component {
render() {
objects.FirstComponent.doSomethingInFirstComponent(); // call the method on the component referred to in 'objects'
return (<div></div>);
}
}
export default SecondComponent ;
When SecondComponent renders, it will trigger the console.log() in FirstComponent.doSomethingInFirstComponent(). This assumes, of course, that FirstComponent is actually mounted.
The "React Guys" that I know seem to think this approach is somehow evil. It uses a simple JavaScript object outside the normal React scope to maintain a reference to any existing objects that I choose to store there. Other than them telling me that "this isn't the way you do things in React", I haven't yet found a good explanation for how this will break or otherwise screw-up my app. I use it as a low-grade replacement for massive-overkill state-management tools like Redux. I also use it to avoid having to pass properties down through dozens of layers of React components just so something at the last level can trigger something waaaaay up in the first level.
That's not to say this approach doesn't have it's problems:
It creates an obvious dependency between the generic objects object, any component that is designed to store a reference to itself inside objects, and any component that wishes to utilizes those references. Then again, using any kind of global state-management solution creates a similar dependency.
It's probably a bad solution if you have any doubt that FirstComponent will be mounted before you try to call it from within SecondComponent.
I've found that just having the reference to a React component won't allow you to do all the things that React components can do natively. For example, it won't work to call objects.FirstComponent.setState(). You can call a method in FirstComponent, which in turn can invoke its own setState(), but you can't invoke FirstComponent's setState() directly from within SecondComponent. Quite frankly, I think this is a good thing.
You can, however, directly access the state values from the components referenced in objects.
This should only be done with "global" components (components that functionally serve as singletons). If, for example, you had a simple UI component called BasicSpan that did little more than render a basic span tag, and you proceeded to use that component over and over again throughout your React app, I'm sure it would quickly become an unmanageable nightmare to try to place references to these simple components in the objects object and then try to intelligently manage calls to those components' internal methods.
you can send an event as props and call it from other component.
Say you have a class
Class A{
handleChange(evt)
{
this.setState({
name:evt.target.value
})
}
render{
return(
<div>
<ComponentB name={this.state.name}{ onChange={this.handleChange}/>
</div>
);
}
}
Child Component
Class B{
handleChange()
{
//logic
}
render{
return(
<div>
<input type="text" onChange={this.props.onChange}/>
{this.props.name}
</div>
);
}
Here in Component B when you change the input it will call the method
of class A and update state of A.
Now getting the updated state as props in component B will give you
the changed text that you just entered

Is constructor needed for a ReactJS component class

I'm using ReactJS in Typescript. Do I need the "Constructor" code below? It works fine without it and I looked at the trans-piled JavaScript and it appears to add it in automatically anyway.
interface myProps {
children?: any;
}
class MyButton extends React.Component<myProps, {}> {
constructor(props: myProps) { //Needed ???
super(props);
}
render() {
return (<div>
<button>
{this.props.children}
</button>
</div>);
} //end render.
} //end class.
No, you don't need to.
In fact, you could write a simple component like this as a function.
const MyButton = (props) => {
return (
<div><button>{props.children}</button></div>
);
};
As mentioned in the accepted answer:
const MyButton = (props) => {
return (
<div><button>{props.children}</button></div>
);
};
This works but it's not the same - not at all. This is constructing a StatelessComponent (without state, without lifecycle-hooks, it's just a plain function, returning only the JSX or the part of the "render" function).
If you need state or lifecycle-hooks, you must extend from React.Component - like it is already done in the question.
To actually answer your question - yes constructor is needed. That's because you are extending of an existing class which is already asking for an initial property ("props") that must be given on the construct -> because it is a react class, react will call internally something like
new MyButton(props);
He will always give you the props object into the instantiated Component. Now, by extending this existing react component class, you must achieve the same - or you will end up missing your "props" object. To make it possible, that one can still pass "props" to your newly defined component, you have to define the props also on your constructor, so you have to write this:
constructor(props: myProps) { .. }
Otherwise, you can't pass anything when you call "new MyButton(props)" -> well, this will not except or bring an error, but "props" will just be "null" in your further code of "MyButton".
Last but not least - you have to call the "super" in an extended version.
super(props);
Otherwise, you won't pass the given "prop" object to the basic class which you're extending from. Here, it could work too without it, but - then "props" will be "null" in any code of "React.Component" itself.
So, yes it is needed!
Basically you can answer this question yourself by simply using a debugger - just open your web-dev tools, jump to your component code, make a breakpoint in your constructor and watch closely what is happending and what is passed ;) Then, delete the property in the constructor once.. and remove the super call once... how will it except/break apart? ;)
Update:
The other question you can always ask yourself when creating new react components: do i need state? do i need lifecycleHooks?
If one of those is yes, you'll have to extend from React.Component - because only the basic class is giving you this sugar. But as can say, no, i don't need any of those - always go with a StatelessComponent:
const MyComp = (props) => (
<div></div>
)
And try to avoid local component states too - you can do much better with one global state trough react-redux and selectors ;)
Constructor and Super are no longer necessary in react. State can be defined without a constructor and lifecycle hooks also work fine. This is a result of Babel transpiling the components that way: http://2ality.com/2017/07/class-fields.html
In traditional ES6 this is currently not the case.
Today, the constructor is needed only when you must initialize a component with a state from props.
import React from 'react'
class AChildComponent extends React.Component {
constructor(props){
super(props)
this.state = {
myState: props.myStateFromProps
}
}
render(){
return(
<div>
<p>{this.state.myState}</p>
</div>
)
}
}
export default AChildComponent

Resources