Pass parent's attributes to child component with children method - reactjs

I have a situation like that:
I call parent modal component with content:
<Modal onClickOnYesButton ={() => console.log('test')}>
<ModalContent />
</Modal>
Modal component itself looks like that:
const Modal = ({ children }) => {
return(
<div>
{children}
</div>
)
}
So modal content component is rendered inside the modal using children.
The question is how could I pass onClickOnYesButton to <ModalContent /> without placing it directly as a ModalContent attribute?

how could I pass onClickOnYesButton to without
placing it directly as a ModalContent attribute
If you want to pass props down to the child you can use React.cloneElement(children, { ...props }) inside of your Modal.
i.e. something like this:
const Modal = ({ children, ...props }) => {
return(
<div>
{ React.cloneElement(children, { ...props }) }
</div>
)
}
Though note that this will only work for a single child, if you have more then you will need to wrap React.cloneElement within React.Children.map.

Can define the method used in onClick as a static method and import the component and access the function with <ComponentName>.<functionName>.
class ModalParent extends React.Component {
render() {
return (
<Modal>
<ModalContents />
</Modal>
);
}
static onClickYes = (e) => {
console.log(e.target, 'yes')
}
}
class ModalContents extends React.Component {
render() {
return (
<div onClick={ModalParent.onClickYes}>Some Modal Contents</div>
);
}
}
class Modal extends React.Component {
render() {
return (
<div className="modal" onClick={ModalParent.onClickYes}>
<h1>Modal</h1>
{this.props.children}
</div>
)
}
}
ReactDOM.render(<ModalParent />, document.getElementById('root'));
.modal {
border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Related

Is there a way of passing props to class component?

I'm a beginner in React.
I still quite don't understand how to pass props to a class component like we do in a function.
Example of a function:
const SurveyFormReview = ({ onCancel, formValues, submitSurvey, history }) => {
return (
...
<button
onClick={() => submitSurvey(formValues, history)}
className="green btn-flat right white-text"
>
...
);
};
Example of a class Component:
class ImageUpload extends Component {
render() {
return (
// I want to use props in here
)
}
}
For example
<ImageUpload propExample="property" />
Inside ImageUpload component you can access it by writing:
this.props.propExample
Just use whatever attributes you want when using the ImageUpload component:
<ImageUpload propA="someValue" propB={someVariable}/>
From the ImageUpload component, just call the props property:
someFunction = () => {
var propAValue = this.props.propA;
var propBValue = this.props.propB;
}
That's it!
You can pass any value as a props in Class and functional components in react. Read more about props
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
};
ReactDOM.render(
<Welcome name="Sara" />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Patterns in React (wrapper)

Good day. I'm building a tree of components and want to use functions of root component in other components of tree. I throw function reference through all tree.
Also I use the object if me need get value from the function in not root componet.
Can you help me?
Can you show me how to do this as HOC ?
If it will be not so hard for you show examples on my code.
import React from 'react';
class Page extends React.Component{
Answer = {
value : ''
}
doSomething(){
console.log(this.Answer.value);
console.log('Ready!');
}
render(){
return(
<div>
<div>
<Body
ParentFunc={()=>this.doSomething()}
ParentParameters={this.Answer}
/>
</div>
</div>
)
}
}
export default Page
class Body extends React.Component{
render(){
const{
ParentFunc,
ParentParameters
} = this.props
return(
<div>
<div>
<SomeComponent
ParentFunc={()=>ParentFunc()}
ParentParameters={ParentParameters}
/>
</div>
</div>
)
}
}
class SomeComponent extends React.Component{
getAnswer(){
const{
ParentFunc,
ParentParameters
} = this.props
ParentParameters.value = 'Some text'
ParentFunc()
}
render(){
return(
<div onClick={()=>this.getAnswer()}>
We can?
</div>
)
}
}
I don't believe a Higher Order Component alone will solve your basic issue of prop drilling. A React Context would be a better fit for providing values and functions generally to "want to use functions of root component in other components of tree".
Context provides a way to pass data through the component tree without having to pass props down manually at every level.
In a typical React application, data is passed top-down (parent to
child) via props, but such usage can be cumbersome for certain types
of props (e.g. locale preference, UI theme) that are required by many
components within an application. Context provides a way to share
values like these between components without having to explicitly pass
a prop through every level of the tree.
Start by creating your Context and Provider component:
const QnAContext = React.createContext({
answer: {
value: ""
},
doSomething: () => {}
});
const QnAProvider = ({ children }) => {
const answer = {
value: ""
};
const doSomething = () => {
console.log(answer.value);
console.log("Ready!");
};
return (
<QnAContext.Provider value={{ answer, doSomething }}>
{children}
</QnAContext.Provider>
);
};
Render QnAProvider in your app somewhere wrapping the React subtree you want to have access to the values being provided:
<QnAProvider>
<Page />
</QnAProvider>
Consuming the Context:
Class-based components consume contexts via the render props pattern.
<QnAContext.Consumer>
{({ answer, doSomething }) => (
<SomeComponent doSomething={doSomething} answer={answer}>
We can?
</SomeComponent>
)}
</QnAContext.Consumer>
Functional components can use the useContext React hook
const SomeComponent = ({ children }) => {
const { answer, doSomething } = useContext(QnAContext);
getAnswer = () => {
answer.value = "Some text";
doSomething();
};
return <div onClick={this.getAnswer}>{children}</div>;
};
Here is where using a Higher Order Component may become useful. You can abstract the QnAContext.Consumer render props pattern into a HOC:
const withQnAContext = (Component) => (props) => (
<QnAContext.Consumer>
{(value) => <Component {...props} {...value} />}
</QnAContext.Consumer>
);
Then you can decorate components you want to have the context values injected into.
const DecoratedSomeComponent = withQnAContext(SomeComponent);
...
<DecoratedSomeComponent>We can with HOC?</DecoratedSomeComponent>
Note: The point of doing all this was to move the values and functions that were previously defined in Page into the Context, so they are no longer passed from Page though Body to SomeComponent (or any other children components).
Demo
Sandbox Code:
const QnAContext = React.createContext({
answer: {
value: ""
},
doSomething: () => {}
});
const QnAProvider = ({ children }) => {
const answer = {
value: ""
};
const doSomething = () => {
console.log(answer.value);
console.log("Ready!");
};
return (
<QnAContext.Provider value={{ answer, doSomething }}>
{children}
</QnAContext.Provider>
);
};
const withQnAContext = (Component) => (props) => (
<QnAContext.Consumer>
{(value) => <Component {...props} {...value} />}
</QnAContext.Consumer>
);
class SomeComponent extends React.Component {
getAnswer = () => {
const { doSomething, answer } = this.props;
answer.value = "Some text";
doSomething();
};
render() {
return (
<button type="button" onClick={this.getAnswer}>
{this.props.children}
</button>
);
}
}
const DecoratedSomeComponent = withQnAContext(SomeComponent);
class Body extends React.Component {
render() {
return (
<div>
<div>
<QnAContext.Consumer>
{({ answer, doSomething }) => (
<SomeComponent doSomething={doSomething} answer={answer}>
We can?
</SomeComponent>
)}
</QnAContext.Consumer>
</div>
<div>
<DecoratedSomeComponent>We can with HOC?</DecoratedSomeComponent>
</div>
</div>
);
}
}
class Page extends React.Component {
render() {
return (
<div>
<div>
<Body />
</div>
</div>
);
}
}
export default function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<QnAProvider>
<Page />
</QnAProvider>
</div>
);
}
Based on your current code I am making the assumption that Body does not modify the values of ParentFunc and ParentParameters before passing them down to SomeComponent.
You have a hierarchy
<Page>
<Body>
<SomeComponent>
</Body>
</Page>
and you want to pass props from Page to SomeComponent without going through Body.
You can do this using children
children is a special prop representing the JSX child elements of the component. We make it so that Body renders the children that it got through props:
class Body extends React.Component{
render() {
return(
<div>
<div>
{this.props.children}
</div>
</div>
)
}
}
We set that children prop by using a <SomeComponent/> element inside of the <Body>:
render() {
return (
<div>
<div>
<Body>
<SomeComponent
ParentFunc={() => this.doSomething()}
ParentParameters={this.Answer}
/>
</Body>
</div>
</div>
);
}
Note that you cannot directly modify the value that you got from the parent, which is what you were doing with ParentParameters.value = 'Some text'. If you want to update the state of the parent then you need to do that through your callback function props. So your code should look something like this:
import React from "react";
class Body extends React.Component {
render() {
return (
<div>
<div>{this.props.children}</div>
</div>
);
}
}
class SomeComponent extends React.Component {
state = {
showAnswer: false
};
onClick() {
// update answer in parent
this.props.setAnswer("Some text");
// change state to reveal answer
this.setState({ showAnswer: true });
}
render() {
return (
<div>
{this.state.showAnswer && <div>Answer is: {this.props.answer}</div>}
<div onClick={() => this.onClick()}>We can?</div>
</div>
);
}
}
class Page extends React.Component {
state = {
value: ""
};
render() {
return (
<div>
<div>
<Body>
<SomeComponent
answer={this.state.value}
setAnswer={(answer) => this.setState({ value: answer })}
/>
</Body>
</div>
</div>
);
}
}
export default Page;

Typescript + React call parent method from child component

I am trying to call parent method from child component, but it doesn't work and method in parent element is not triggered. In this example I have only two components where ChildHello calls method in Hello component.
codesandbox
Hello.tsx
import * as React from "react";
interface Props {
itemClicked: () => void;
}
export class Hello extends React.Component<Props, {}> {
constructor(props: Props) {
super(props);
}
itemClicked = val => {
console.log(val);
};
render() {
const { name } = this.props;
return <h1 itemClicked={this.itemClicked}>{this.props.children}</h1>;
}
}
const styles = {
height: "400px"
};
export class ChildHello extends React.Component<Props, {}> {
constructor(props: Props) {
super(props);
}
render() {
return (
<div onClick={this.props.itemClicked} style={styles}>
<Hello>Hello Child</Hello>
</div>
);
}
}
You need to understand parent child relationship
in childHello you are using the click event.
<div onClick={this.props.itemClicked} style={styles}>
<Hello>Hello Child</Hello>
</div>
childhello is called by the index.jsx page
<div style={styles}>
<ChildHello name="CodeSandbox" />
</div>
Here you are not passing any click events. also, hello component is inside the child component which is wrong.
All the parent component should have click method involved and that method should be passed as props.
Like this
Parent:
<div style={styles}>
<Hello name="CodeSandbox" />
</div>
Hello component
render() {
const { name } = this.props;
return <ChildHello itemClicked={this.itemClicked} />;
}
ChildHello
render() {
return (
<div onClick={this.props.itemClicked} style={styles}>
Hello
</div>
);
}
Sandbox demo

How do I pass up the value inside of react-autosuggest to the parent component?

In my app.js, I have this
render() {
return (
<div id="App">
<SearchBar />
</div>
);
}
And inside of SearchBar, I import react-autosuggest and have this -
render() {
const { value, suggestions } = this.state;
const inputProps = {
placeholder: "Search",
value,
onChange: this.onChange
};
return (
<Autosuggest
style={style}
suggestions={suggestions}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
getSuggestionValue={getSuggestionValue}
renderSuggestion={renderSuggestion}
inputProps={inputProps}
onSubmit={this.doSomething}
/>
);
}
Where all these functions are standard boilerplate functions that react-autosuggest uses. How do I access what was searched for inside of SearchBar inside of it's parent, app.js?
You can use props to raise the data from the Autosuggest events to the parent component. Create a method inside App and pass it down as a prop to the SearchBar component. Then, call it with the Autosuggest event.
App
class App extends React.Component {
onSubmit(e) {
// `e` is the data that was passed through from the `Autosuggest` event.
console.log(e);
}
render() {
return (
<div id="App">
<SearchBar onSubmit={this.onSubmit} />
</div>
);
}
}
SearchBar
<Autosuggest onClick={this.props.onSubmit} />
You can use a callback function at the SearchBar component. Follow the steps below according to your use case:
Add below function at SearchBar component
onHandleChange(event, value){
this.setState({ value });
this.props.getInputData(event, value);
}
Use above function at AutoSuggest component
return (
<Autosuggest
...
onChange={(event, value) => this.onHandleChange(event, value)}
...
/>
);
Use SearchBar component like
render() {
return (
<div id="App">
<SearchBar getInputData={(event, value) => this.getAutosuggestInput(event, value)}/>
</div>
);
}
Here you get the value of input box at the parent component
getAutosuggestInput = (event, value) => {
//add your logic here
}

Can we pass event name as props in react?

Hi I am new to react and I am trying to create a component where we can pass event name(onclick, onChange etc.) as props. So the component can be customize in an event way as well. Is it possible?
<Input {this.props.eventName} = {this.props.name} />
This I want to do. Is it possible?
Do you want to achieve something similar to this -
One problem is that you must pass only supported events to the element type.
e.g in case of button onClick and other events supported by button.
class Parent extends React.Component {
render() {
return(
<ChildComponent
evtName = 'onClick'
evtHandler={ () => { console.log("event called!");}}
/>
)
}
}
class ChildComponent extends React.Component {
render() {
return React.createElement(
'button',
{ [this.props.evtName] : this.props.evtHandler },
'Click me'
); }
}
ReactDOM.render(
<Parent />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
If I understand you correctly, just pass the event and it's handler as props. I didn't see the use case just considering the event name.
See the below example of reusing the same element with different events.
class Input extends React.Component {
render(){
const {} = this.props;
return (
<input {...this.props} />
);
}
}
class Test extends React.Component {
render(){
return (
<div>
<Input name="onClick" onClick={(e) => console.log(e.target.name)}/>
<Input name="onChange" onChange={(e) => console.log(e.target.name)}/>
</div>
);
}
}
ReactDOM.render(<Test />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Here is example how to pass an event from parent Component to Child - by having result in Parent Component.
class Parent extends Component {
//constructor, other methods, etc...
// We call our event update
update(stuff) {
console.log(stuff);
}
}
We pass in ParentComponent's render method a ChildComponent with props onClick(you can name it whatever you want).
<ChildComponent
onClick={this.update.bind(this)}
/>
Now, ChildComponent. To access our props onClick, we just use this.props.onClick. Our argument is just hello, you can pass as many arguments you want.
<button onClick={ (e) => this.props.onClick('hello') }>
Action
</button>
Here is working example:
class Parent extends React.Component {
update(stuff) {
console.log(e, stuff);
}
render() {
return(
<ChildComponent
onClick={this.update.bind(this)} />
)
}
}
class ChildComponent extends React.Component {
render() {
return(
<div>
<button onClick={ (e) => this.props.onClick('hello') }> Action</button>
</div>
)
}
}
ReactDOM.render(
<Parent />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

Resources