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>
Related
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>
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>
I want on every component in my app to add attribute component-name="componentX". I don't want to do it hard coded when creating a component but to find a generic way to find the component name and set it in the attribute.
I know there is the displayName property but it's only for class component.
I have a lot of functional components. How can I achieve it?
Thanks!
If you wrap children you can access the child.type.name to get the component / function name.
Here is a small example:
class LogNames extends React.Component {
render() {
const { children } = this.props;
React.Children.forEach(children,child =>{
console.log(child.type.name)
});
return children;
}
}
class Child extends React.Component {
render() {
return <div>A Child</div>
}
}
const Child2 = () => <div>Child 2</div>
class App extends React.Component {
render() {
return (
<div>
<LogNames>
<Child/>
</LogNames>
<LogNames>
<Child2 />
</LogNames>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<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" />
Just noticed you want to set an attribute on the DOM as well, so i added another example that does just that (open devtools to see the attributes):
class WithNameAttr extends React.Component {
componentDidMount(){
const {children} = this.props;
// get a ref to the current node
const node = ReactDOM.findDOMNode(this);
// force and return a signle child
const child = React.Children.only(children);
// set name attribute (if available)
const name = child.type.name;
name && node.setAttribute('component-name', name);
}
render() {
return this.props.children;
}
}
class Child extends React.Component {
render() {
return <div>A Child</div>
}
}
const Child2 = () => <div>Child 2</div>
class App extends React.Component {
render() {
return (
<div>
<WithNameAttr>
<Child />
</WithNameAttr>
<WithNameAttr>
<Child2 />
</WithNameAttr>
<WithNameAttr>
<div>dom elemetns doesnt have names</div>
</WithNameAttr>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<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"/>
I have the parent component called App. It holds the state and the form which changes the state.
I removed the unimportant code. When I type something in the form, an object item is added in the state.item but the ToCompleteItemsComponent doesn't update. Is there a way to update that component on state change
class App extends Component {
state = {
items: []
}
submitItem=(e)=> {
e.preventDefault();
let items = this.state.items;
items.push({
name: this.state.ValuePlaceholder,
completed: false
});
this.setState({
items
});
}
render() {
return (<BrowserRouter>
<div className="wrap">
...
<form onSubmit={this.submitItem}>
<input type="text" />
</form>
<Route exact path="/"
render={props => <ToCompleteItemsComponent
items={this.state.items} /> }
/>
</div>
</BrowserRouter>
);
}
}
Edit This is the component
class ToCompleteItemsComponent extends Component {
componentWillReceiveProps(nextProps) {
// check this.props vs nextProps and setState!
// do whatever you want.
console.log(nextProps)
}
render(){
return(<div>
<ul className="items">
{
this.props.items.map( (item,id)=> <li key={id}>{item.name} <span>x</span></li>)
}
</ul>
</div>)
}
}
export default ToCompleteItemsComponent;
console.log(nextProps) returns an empty array
When the parent state updates you can use: componentWillReceiveProps
in ToCompleteItemsComponent you can do this:
componentWillReceiveProps() is invoked before a mounted component
receives new props. If you need to update the state in response to
prop changes (for example, to reset it), you may compare this.props
and nextProps and perform state transitions using this.setState() in
this method.
componentWillReceiveProps(nextProps) {
// check this.props vs nextProps and setState!
// do whatever you want.
}
As you can see in this snippet the console.log works as intended. I reworked the way you add stuff to your items array. Please have in mind I removed the exact attribute from the Route because in snippets I can't use Routes properly with BrowserRouter.
class App extends React.Component {
state = {
items: []
}
submitItem=(e)=> {
e.preventDefault();
this.setState({
items: this.state.items.concat([{
name: this.state.ValuePlaceholder,
completed: false
}])
});
}
render() {
return (<ReactRouterDOM.BrowserRouter>
<div className="wrap">
<form onSubmit={this.submitItem}>
<input type="text" />
<button type="submit">submit</button>
</form>
<ReactRouterDOM.Route path="/"
render={props => <ToCompleteItemsComponent
items={this.state.items} /> }
/>
</div>
</ReactRouterDOM.BrowserRouter>
);
}
}
class ToCompleteItemsComponent extends React.Component {
componentWillReceiveProps(nextProps) {
// check this.props vs nextProps and setState!
// do whatever you want.
console.log("HALO", this.props, nextProps)
}
render(){
return(
<div>
<ul className="items">
{
this.props.items.map( (item,id)=> <li key={id}>{item.name} <span>x</span></li>)
}
</ul>
</div>
)
}
}
ReactDOM.render(<App />, 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>
<script src="https://unpkg.com/react-router/umd/react-router.min.js"></script>
<script src="https://unpkg.com/react-router-dom/umd/react-router-dom.min.js"></script>
<div id="root"></div>
I have a button in a parent component. I want to focus an input field, which is located in a child component, by clicking on that button. How can I do it.
You can make use of refs to achieve the result
class Parent extends React.Component {
handleClick = () => {
this.refs.child.refs.myInput.focus();
}
render() {
return (
<div>
<Child ref="child"/>
<button onClick={this.handleClick.bind(this)}>focus</button>
</div>
)
}
}
class Child extends React.Component {
render() {
return (
<input type="text" ref="myInput"/>
)
}
}
ReactDOM.render(<Parent/>, document.getElementById('app'));
<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="app"></div>
UPDATE:
React docs recommend on using a ref callback rather than string refs
class Parent extends React.Component {
handleClick = () => {
this.child.myInput.focus();
}
render() {
return (
<div>
<Child ref={(ch) => this.child = ch}/>
<button onClick={this.handleClick.bind(this)}>focus</button>
</div>
)
}
}
class Child extends React.Component {
render() {
return (
<input type="text" ref={(ip)=> this.myInput= ip}/>
)
}
}
ReactDOM.render(<Parent/>, document.getElementById('app'));
<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="app"></div>
Instead of accessing the child component's input element from parent, it's better to expose a method as below,
class Parent extends React.Component {
handleClick = () => {
this.refs.child.setFocus();
};
render() {
return (
<div>
<Child ref="child"/>
<button onClick={this.handleClick.bind(this)}>focus</button>
</div>
)
}
}
class Child extends React.Component {
setFocus() {
this.refs.myInput.focus();
}
render() {
return (
<input type="text" ref="myInput"/>
)
}
}
ReactDOM.render(<Parent/>, document.getElementById('app'));
<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="app"></div>