So in my main component, I send props to a child component like this:
<div className={styles.navBar}>
<MasterNavBar color={false} scrollChange={true} />
</div>
MasterNavBar gets access to the props correctly, but I need MasterNavBar to pass those props to a child component called NavBar, which I currently do like this:
<NavBar props />
However, when I do this, the props are not accessible in my NavBar component file. For example, in NavBar, doing props.color to get my color prop returns undefined. So, am I passing my props incorrectly, or am I missing something else?
As Antonio Pantano said, you can pass the whole props to the <NavBar {...props}/> component, but this is a bad practice way. This article explained why it is a bad practice way. So you can pass the MasterNavBar props to its child in these two ways:
const MasterNavBar = (props) => {
/*
rest of your code
*/
return <NavBar color={props.color} scrollChange={props.scrollChange}/>
}
or
const MasterNavBar = ({color, scrollChange}) => {
/*
rest of your code
*/
return <NavBar color={color} scrollChange={scrollChange}/>
}
You can change the name of NavBar props as you wish, and you access them in the NavBar component by these names.
That's depend on which props NavBar is expecting. If you don't care about typing (either with typescript or react PropTypes) you can simply do like this:
<NavBar {...props} />
However this is not the best approach and can lead to performance issues.
Related
Let's say I have a component with a scrollable subcomponent, and I want to expose the ability to scroll:
const MyComponent = (props) => {
return <ScrollView ... />
}
I want to be able to do
<MyComponent ref={myRef} />
...
myRef.scrollTo({x: 0});
So I need a way to forward the ref to the <ScrollView>. Let's try putting the ref on the props:
const MyComponent = (props) => {
return <ScrollView ref={props.scrollRef} ... />
}
...
<MyComponent scrollRef={myRef} />
...
myRef.scrollTo({x: 0});
I just tried that with React Native on iOS, and it indeed works. I see several advantages over React.forwardRef:
Simpler, because I don't need to use another React API.
Works also if there is more than one child who needs ref forwarding.
Seems to me that this approach is
What's the advantage of React.forwardRef? Why was it added in React 16.3?
Note that there is no difference between using another named prop like innerRef FOR FORWARDING, it works the same.
Refactoring class components
Since React moved toward function components (hooks) you might want to refactor the class component code to a function component without breaking the API.
// Refactor class component API to function component using forwardRef
<Component ref={myRef} />
React.forwardRef will be your only option (further explained in details).
Clean API
As a library author you may want a predictable API for ref forwarding.
For example, if you implemented a Component and someone wants to attach a ref to it, he has two options depending on your API:
<Component innerRef={myRef} />
The developer needs to be aware there is a custom prop for forwarding
To which element the innerRef attached? We can't know, should be mentioned in the API or we console.log(myRef.current)
<Component ref={myRef} />
Default behavior similar to ref prop used on HTML elements, commonly attached to the inner wrapper component.
Notice that React.forwardRef can be used for function component and HOC (for class component see alternative below).
Ref forwarding is not limited to DOM components. You can forward refs to class component instances, too.
For function components, forwardRef sometimes comes with useImperativeHandle combo (in class component you just call the class methods on ref instance: ref.current.myAttr().
// Same usage
<Component ref={myRef} />
const Component = React.forwardRef((props, ref) => {
// you can forward ref <div ref={ref} />
// you can add custom attributes to ref instance with `useImperativeHandle`
// like having ref.myAttribute() in addition to ones attached to other component.
});
Important behavior of ref prop without forwardRef.
For the class component, this code alone will attach the ref to CLASS INSTANCE which is not useful by itself and need another ref for forwarding:
// usage, passing a ref instance myRef to class Component
<Component ref={myRef} />
Full example, check the logs:
// We want to forward ref to inner div
class ClassComponent extends React.Component {
innerRef = React.createRef();
render() {
// Notice that you can't just `this.props.ref.current = node`
// You don't have `ref` prop, it always `undefined`.
return <div ref={this.innerRef}>Hello</div>;
}
}
const Component = () => {
const ref = React.useRef();
useEffect(() => {
// The ref attached to class instance
console.log(ref.current);
// Access inner div through another ref
console.log(ref.current.innerRef);
}, []);
return <ClassComponent ref={ref} />;
};
In function components, it won't even work because functions don't have instances.
By default, you may not use the ref attribute on function components because they don’t have instances. [1]
forwardRef.
Refs and the DOM.
Why we need ref forwarding?
Is it possible to pass a prop containing a function to any child ? More precisely I would like to pass a function from a component to every {children} component :
<>
<Navbar />
<main>{children}</main>
<Footer />
</>
You should then use something like Context for that.
The props you pass to your Context Provider will be available to all it's children. But you won't get them via props, you need to get them via the Context Consumer.
But the good thing is Context Consuming hooks are available to make it easy to do so.
Currently working a group project and I've been looking over the code the group wrote and I'm curious if there is a better way to pass props to n children components that change the state of the top-level App component.
currently we have something like this:
return (
<div>
<header className="navBar">
// NavBar is an example for the question
<NavBar
showSplash={this.state.showSplash}
displayAllSearchResults={this.displayAllSearchResults}
searchBarDisplay={this.state.showCard}
updateResults={this.updateResults}
selectResult={this.selectResult}
searchResults={this.state.searchResults}
showSuggestions={this.state.showSuggestions}
/>
</header>
<section className="App">
<article className="mainContent">{card}</article>
</section>
<div className="appBackground" />
</div>
);
All of these methods (which there are probably too many) are being passed into the NavBar component that itself is composed with a SearchBar component which also needs that giant block passed into it in order to change the state all the way back at the top-level App. This feels wrong, is there a solution or better approach to passing all these props down?
You can use the spread operator like this :
<NavBar {...this.state} />
It will pass you state properties as props to the navbar.
In you Navbar component, you can get the value of showSplash in state from props.showSplash
If you want to pass the data into a child component of Navbar, you can do something like this :
<ChildComponent {...props} /> // if Navbar is a functional component
<ChildComponent {...this.props} /> // if Navbar is a stateful component
Reference:
Spread in object literals
Here is a sample stackblitz for illustration.
If you want a data to be accessible by all the child components, then you have following options:
React Context
Redux or state management library like flux etc.
Or you could follow #Abdelkarim EL AMEL answer if that soughts out you issue for now.I would suggest take a look at the above mentioned options and if setting a global state which can be accessed anywhere in the app helps you to not send prop to each component explicitly. I would prefer to use a global state for variable which are common and needs to be accessed by all the child of the root app.
For example a button component to change the theme of the app. It changes the state of the root app and is accessible by every component.
I think this is actually entirely OK, and would recommend leaving it.
If you don't like seeing it itemized in the return of the render method you could keep them all in an object called navbarprops and just spread that in. I still prefer it as is though!
I have a bunch of components that require the same prop, and going through them individually to add the JSX is cumbersome, can I create a piece of JSX in the parent that will be passed to these child components as a prop?
Here's basically what I want to do:
function App(props) {
/*not part of the actual code, but I want to
pass this h1 tag into the bottom components as a prop*/
<h1>{props.heading}</h1>
<Home heading="Home"/>
<AboutMe heading="About Me"/>
<Contact heading="Contact"/>
}
I know the code above isn't how you're supposed to do it, but is there a way I can accomplish that functionality?
Yes, that's possible. Just assign your JSX component to a variable and pass that variable as a prop. You have the right idea.
For example:
var customHeader = <h1>Here's a custom component</h1>
You can also set customHeader to a React component such as: var customHeader = <CustomHeader />
You can pass through <MyComponent header={customHeader}/> and in your render function of MyComponent page, you can simply use {this.props.header} to load your component. This can be repeated for any number of components.
Edit based on comments. In that case, I would wrap the component in a function. For example:
getCustomHeader = (title) => {
return <MyHeader
headerTitle={title}
/>
}
Now, in your render function, you can call getCustomHeader.
render() {
return <div>
<MyComponent1 header={this.getCustomHeader("Title A")} />
<MyComponent2 header={this.getCustomHeader("Title B")} />
</div>
}
So I am following some tutorial and I am confused regarding how things render when using HOC
So firstly, I guess props flow from parent to child and is one directional?
Here we created two HOC, The Aux and withclass
The Aux doesn't do anything special besides passing props.children
const aux = (props) => props.children;
export default aux;
The withClass HOC function takes two parameter App and className..
const withClass = (WrappedComponent, className) => {
return class extends Component {
render () {
return (
<div className={className}>
<WrappedComponent {...this.props} />
</div>
)
}
}
And our App.js which is passed as an argument looks like this
import React, { PureComponent } from 'react';
import classes from './App.css';
import Persons from '../components/Persons/Persons';
import Cockpit from '../components/Cockpit/Cockpit';
import Aux from '../hoc/Aux';
import withClass from '../hoc/withClass';
class App extends PureComponent {
//something
render () {
if ( this.state.showPersons ) {
persons = <Persons
persons={this.state.persons}
clicked={this.deletePersonHandler}
changed={this.nameChangedHandler} />;
}
return (
<Aux>
<button onClick={() => { this.setState( { showPersons: true } ) }}>Show Persons</button>
<Cockpit
appTitle={this.props.title}
showPersons={this.state.showPersons}
persons={this.state.persons}
clicked={this.togglePersonsHandler} />
{persons}
</Aux>
);
}
}
export default withClass( App, classes.App );
[Question] So based on the above code if someone can please explain what exactly happens, the way things execute and render?
Secondly, We used {...this.props} in our withClass HOC because according to the instructor they are wrapped and hence our other component, even though they receive prop they can't pass them. Can someone explain this with an example? Also {...this.props} creates copy of all the props? and shouldn't it be like <WrappedComponent = {...this.props} /> instead of <WrappedComponent {...this.props} />
First of all, what is a HOC?
A HOC is a Higher-order component. This means it is a function that takes as its first argument a component and then returns a component.
From this definition you can immediately see that:
withClass is an HOC
Aux is not an HOC
Aux is a functional component. Classic React components are created by defining a class that inherits from React.Component. A newer, simpler way of defining components is to create functions that simply accept props as the first parameter and return what should be rendered.
So based on the above code if someone can please explain what exactly happens, the way things execute and render?
Well, let's look at App just as a component. We have withClass and App and you're exporting withClass(App, classes.App). What would it look like if, instead of using an HOC we used a functional component? It'd look like this:
const AppWithClass = props =>
<div className={classes.App}>
<App/>
</div>
In this case, no props are passed to App. So with this use-case, there is no need to pass props through by writing {...props}. And you'd then simply export AppWithClass.
But usually you write HOCs to be reusable. In that case, you don't know if the component that will be passed to your HOC will receive props or not. For that reason, you want the component you create to take any props passed to it and to pass them through to the wrapped component.
Let's say you have a Button component that takes a parameter colour. You'd typically use it like this:
<Button colour="red"/>
But you want to wrap it with a div and add a class to that div. So you use withClass as follows:
const ButtonWithClass = withClass(Button, "button-class");
Now you can use Button as follows:
<ButtonWithClass colour="red"/>
And really what you'll get is:
<div className="button-class">
<Button colour="red"/>
</div>
If you did not write {...this.props} when rendering WrappedComponent in your withClass HOC, then colour would not get passed through to Button. In your HOC, WrappedComponent is equal to Button in the above case.
The syntax {...this.props} is a combination of the Object literal spread syntax and JSX's own behaviour. The Object spread syntax used in this way means the keys of the given object will become the prop names and the values will become their respective values. So when you write {...{ colour: 'red' }} you're asking JSX to get props from an object that you define inline.
To continue with the above example:
<ButtonWithClass colour="red"/>
Inside withClass this becomes equivalent to:
const WrappedComponent = Button;
return <WrappedComponent {...this.props}/>;
And here this.props equals { colour: 'red' }. So the above becomes:
const WrappedComponent = Button;
return <WrappedComponent {...{ colour: 'red' }}/>;
Which then becomes:
const WrappedComponent = Button;
return <WrappedComponent colour="red"/>;
I hope that helps!