onclick event for Imported component in react? - reactjs

I have imported a component from a different file and I want to reset my timer if I click on the imported component's elements. Is there a way to tackle this issue or should I write both components in single jsx ?
import {SampleComponent} from "../SampleComponent";
<div>
<SampleComponent onClick = {?????????}/>
</div>

What you can do here is,
import {SampleComponent} from "../SampleComponent";
<div onClick={??????}>
<SampleComponent/>
</div>
Or you can pass the function from your parent component and add click event on top node of the child component.
<div>
<SampleComponent onHandleClick={() => ??????}/>
</div>

If you want to call a function in parent component, whenever an event (such as in your case an onClick event) occurs in a child component, you will need to pass the parent function as a props.
Here's what it will look like:
class ParentComponent extends React.Component {
handleClick = () => { ... }
render {
return (
<SampleComponent onClick={this.handleClick} />
)
}
}
And here is how your SampleComponent will be:
class SampleComponent extends React.Component {
render {
return (
<div onClick={this.props.onClick}> //to call the function on whole component
<button onClick={this.props.onClick}>Click Me</button> //to call the function on a specific element
</div>
)
}
}
What I have understand so far from your question is that you want to call a function in SampleComponent whenever a click event occurs on it (on SampleComponent).
To do this, here is how your SampleComponent will look like :
class SampleComponent extends React.Component {
.
.
render() {
handleClick = () => { ... }
return (
<div onClick={this.handleClick}>
...
</div>
)
}
Note: For this you don't need to add onClick in parent.

resetTimerHandler = (timer)=>{
timer = 0; or this.setState({timer: 0}) // how ever you are defining timer
}
render(){
let displayTimer;
this.updateTimer(displayTimer)// However you are updating timer
return(
<SampleComponent onClick={this.resetTimerHandler.bind(this,displayTimer)} />)
Without knowing how you are updating your timer I can't really give a specific answer but you should be able to apply this dummy code.

It's hard to answer your question specifically without more context (like what timer are you wanting to reset). But the answer is no, you do not need to implement both components in the same file. This is fundamental to react to pass props like what you tried to do in your question.
Here is an example.
Say your SampleComponent looks like the following:
// SampleComponent.jsx
function SampleComponent({ onClick }) { // the argument is an object full of the props being passed in
return (
<button onClick={(event) => onClick(event)}>Click Me!</button>
);
}
and the component that is using SampleComponent looks like this:
import SampleComponent from '../SampleComponent';
function resetTimer() {
// Add logic to reset timer here
}
function TimerHandler() {
return (
<SampleComponent onClick={() => resetTimer()} />
);
}
Now when you click the button rendered by SampleComponent, the onClick handler passed from TimerHandler will be called.
A prop on a React component is really just an argument passed into a function :)

Related

How can I remove Alert function from ListIem component?

I have a component (ListItem.js), which shows some photos from an API request.
Inside the same component I have a function called showAlert, that also uses some data from the API request and shows them in an alert dialog.
To invoke the showAlert function I use this line:
onClick={() => this.showAlert()}
This is the whole component (simplified):
import React, { Component } from "react";
import SweetAlert from "react-bootstrap-sweetalert";
class ListItem extends Component {
state = {
alert: null // initialising an empty alert
};
showAlert() {
const getAlert = () => (
<SweetAlert confirmBtnBsStyle="info" onConfirm={() => this.closeAlert()}>
<img src={this.props.photo.urls.small} />
</SweetAlert>
);
this.setState({
alert: getAlert() // Fire up the dialog box
});
}
closeAlert() {
this.setState({
alert: null // colse the dialog window
});
}
render() {
return (
<>
<div className="card__body">
<img
src={this.props.photo.urls.small}
onClick={() => this.showAlert()}
/>
</div>
{this.state.alert}
</>
);
}
}
export default ListItem;
I want to remove the showAlert function and put it in a separate component, so I can use it in different components.
For this I created a new component called Alert.js, cut the showAlert function and the state, put them inside the Alert.js and exported it.
Then inside ListItem.js, I imported the Alert component and replaced this line:
{this.state.alert}
With this line:
<Alert photoPath={this.props.photo.urls.small} />
But what I don't know is, how can I invoke showAlert funcion, which is inside the Alert.js (child component) from the ListItem.js (parent component)?
The question is: How can I invoke a function inside the child component from the parent component?
There is no way to trigger function calls from the parent. In that situation, what you want to do is pass the function showAlert as a prop to the Alert component, and bind this to the parent component and trigger it when needed in the parent.

Reactjs focus an input field located in a child component which is inside another child component

I am currently trying to focus an input field that is located in a child component which is inside another child component. consider the following,
Parent.js
child1.js
child2.js
I would like to focus input field in child2 when a button is clicked from parent.js.
I don't say using this technique it's good but you can achieve this by creating a setRef function who get pass by the child 1 to the child 2. Make sure to read this https://reactjs.org/docs/refs-and-the-dom.html why refs is not the best thing. For me I would use props callback.
But if you really want to use ref this is how you can do. I put the code example here. You can also play with the code here https://codesandbox.io/s/5x8530j3xn
class Child2 extends React.Component {
render() {
return (
<div>
<input ref={this.props.setRef} />
</div>
);
}
}
class Child1 extends React.Component {
render() {
return (
<div>
<Child2 setRef={this.props.setRef} />
</div>
);
}
}
class Parent extends React.Component {
setRef = ref => {
this.input = ref;
};
focus = () => {
this.input.focus();
};
render() {
return (
<div>
<Child1 setRef={this.setRef} />
<button onClick={this.focus}>Go Focus</button>
</div>
);
}
}
Just use plain vanilla JavaScript inside a React method.
handleFocus () {
document.getElementById('inputField').focus()
}
<button onClick={this.handleFocus}>My Button</Button>
This will focus the input after clicking the button.

Get the child's props onClick in parent component

I have a parent ButtonGroup component and the child buttonItem component:
//ButtonGroup Component (Parent)
clicky(){
//capture the props of the clicked button, ie, caption and disabled here.
}
render() {
return (
<div onClick={this.clicky}>
{this.props.children}
</div>
)
}
//buttonItem component:
render() {
return (
<button disabled={this.props.disabled}>{this.props.caption}</button>
)
}
//final render
<ButtonGroupComponent>
<buttonItem caption="Nothing"/>
<buttonItem caption="Something" disabled={true}/>
<buttonItem caption="Refresh"/>
</ButtonGroupComponent>
from the above code is there any way i can capture the props of the clicked child buttonItem?
In your case, you need to merge this.props.children with your custom prop. So, I suggest you use React.Children to operate with it.
By the way, after adding new prop you need to return this child, so cloneElement will help you with this.
Inside import section of ButtonGroupComponent:
import React, { Children, Component, cloneElement } from 'react';
Its render function will look like this:
render() {
const childrenWithCustomHandler = Children.map(this.props.children, itemChild => {
// Arguments of cloneElement (component, [customProps], [customChildren])
return cloneElement(itemChild, { onClickItem: this.clicky })
}
);
return <div>{childrenWithCustomHandler}</div>;
}
And the code of buttonItem component will look like:
return (
<button
disabled={this.props.disabled}
onClick={() => {
this.props.onClickItem({ ...this.props });
}}
>
{this.props.caption}
</button>
)
I used Spread operator to clone the object, so if you will want to change props in your clicky function, the child won't be rendered.

Using React, is it possible to define a component inside the definition of another component?

(this question differs to 'is it possible to use a component inside another', this question is 'can I define a component inside the definition of another', it is not a duplicate of 'Can I write Component inside Component in React?')
Is it possible to define a component inside the definition of another component? This way I can use the props of the outside component in the inner component. It would keep the code more concise. Something like the following...
class AComponent extends Component {
CustomButton = (props) => {
let { disabled, ...otherProps } = props // <-- props of the inner component
const {isDisabled, currentClassName} = this.props // <-- props of the main component
return (
<button
className={className}
disabled={isDisabled}
{...otherProps}>
</button>
)
}
render() {
return (
<div>
<CustomButton>Add something</CustomButton>
<CustomButton>Delete something</CustomButton>
<CustomButton>Edit</CustomButton>
</div>
)
}
}
If the custom button was defined on its own (the usual way of defining components) I would have to do something like below which is ok but more verbose and less dry as I repeat the definition of {...buttonProps} for each component
let buttonProps = {
className: this.props.currentClassName,
disabled: this.props.disabled
}
return (
<div>
<button {...buttonProps}>Add something</button>
<button {...buttonProps}>Delete something</button>
<button {...buttonProps}>Edit</button>
</div>
)
While yes, it's possible to define one function component inside another function component, this is not recommended.
Referring to the ReactJs docs:
reactjs.org/docs/components-and-props
reactjs.org/docs/conditional-rendering
Most, if not all, examples show child components being defined outside of the parent component.
Defining a component inside another will cause the child component to be re-created on mount and unmount of the parent, which could cause unexpected behavior if the child is using props from the parent, and cannot handle if those props are suddenly undefined.
It's best to define components separately.
Yes! I needed to define the function component in the render method...which makes sense
class AComponent extends Component {
render() {
const ButtonLocal = (props) => {
const {isDisabled, currentClassName} = this.props
return (
<Button
disabled={isDisabled}
className={currentClassName}
{...buttonProps}>
</Button>
)
}
return (
<div>
<ButtonLocal>Add something</ButtonLocal>
<ButtonLocal>Delete something</ButtonLocal>
<ButtonLocal>Edit</ButtonLocal>
</div>
)
}
}
Very much possible to add components inside a parent component. Consider following example:
<ParentComponent style={styles}>
<ChildComponent style={styles} {...props} />
</ParentComponent>

Updating Child Component Via Change of Props from Parent in React

I have a component whose state is passed as a prop to a child component. I noticed that even if the Parent's state is changed, the child component is not re-rendered.
Based on the official React Documentation for utilizing shouldComponentUpdate:
"The first time the inner component gets rendered, it will have { foo:
'bar' } as the value prop. If the user clicks on the anchor, the
parent component's state will get updated to { value: { foo: 'barbar'
} }, triggering the re-rendering process of the inner component, which
will receive { foo: 'barbar' } as the new value for the prop.
The problem is that since the parent and inner components share a
reference to the same object, when the object gets mutated on line 2
of the onClick function, the prop the inner component had will change.
So, when the re-rendering process starts, and shouldComponentUpdate
gets invoked, this.props.value.foo will be equal to
nextProps.value.foo, because in fact, this.props.value references the
same object as nextProps.value.
Consequently, since we'll miss the change on the prop and short
circuit the re-rendering process, the UI won't get updated from 'bar'
to 'barbar'."
If I can't use shouldComponentUpdate, how would I force the child component to re-render based on a change of props from the Parent?
Parent Component
I want the child component to rerender based on the boolean given to showDropzone.
<Dropzone
onDrop={this.onDrop}
clearFile = {this.clearFile}
showDropzone = {this.state.filePresent}/>
Child Component
export default class Dropzone extends Component {
constructor(props) {
super(props);
}
render() {
const { onDrop } = this.props;
const {showDropzone} = this.props;
const {clearFile} = this.props;
return (
<div className="file-adder">
<div className="preview">
{{showDropzone}?
<Dropzone
onDrop={onDrop}
ref="dropzone">
</Dropzone>
:<button type="button"
className="btn btn-primary"
onClick={clearFile}>
Clear File</button>}
</div>
</div>
);
}
}
I'am sorry but your code has a lot of errors
First (This is a tip) inside:
const { onDrop } = this.props;
const { showDropzone } = this.props;
const { clearFile } = this.props;
You can write just one line
const { onDrop, showDropzone, clearFile } = this.props;
Now your problems starts from here:
You used showDropzone inside curly brackets and this is your
problem remove them
Your mistake {{showDropzone}? firstOption : anotherOption }
Will be { showDropzone? firstOption : anotherOption }
Another mistake:
You used the Dropzone component inside itself and this is a big mistake
You can't use Dropzone component here {{showDropzone}? <Dropzone {...}/> : anotherOption } You can use it from another component
Finally i tried to format your code to make it like this
Parent Component
{ this.state.filePresent
? <Dropzone onDrop={this.onDrop} />
: <button
type="button"
className="btn btn-primary"
onClick={clearFile}
children="Clear File"
/>
}
Child Compnent
class Dropzone extends Component {
render() {
const { onDrop } = this.props;
return (
<div className="file-adder">
<div className="preview" onDrop={onDrop} ref="dropzone">
<p>Hi! This is Dropzone component</p>
</div>
</div>
);
}
}
export default Dropzone;
In child component you have used "showZone" instead of "showDropzone".
Thanks

Resources