How can I remove Alert function from ListIem component? - reactjs

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.

Related

How can I handle an onsubmit event in a parent component from the button?

In my Parent component, there is a form and for submit button it is mentioned in child component.
For example, on hyperlink in the parent component (which is having lots of input data), I'm displaying a popup which is a child component and in this popup there is a submit button, which is also in the child component.
How can I handle OnSubmit method of the parent component from the child component's button click?
Did you store and maintain all input data in parent? If No, you need to store input data in parent component (or using Context Api and store data there)
Create handleSubmit in parent component and pass it as a callback function from parent to button click component, when button is clicked, call the callback function to trigger a handleSubmit function in parent.
Simple demo
import React, { useState } from "react";
export default function App() {
// store all form data in this parent
const [formData, setFormData]= useState({})
const handleSubmit=() =>{
// handle and summit your formData here
}
return (
<div className="App">
{/* Crate a Form compoent and render it here */}
<Button onSubmit={handleSubmit}/>
</div>
);
}
export const Button = ({onSubmit}) =>{
return(
<button onClick={onSubmit}>Submit</button>
)
}
Here is another solution to call parent component function using React props(Property).
[React Property:][1]https://reactjs.org/docs/components-and-props.html
// Parent Component
import React from "react";
// import child component
class parent extends React.Component{
parentClassFunction = () => {
console.log("parent called")
};
render(){
<Child
parentClassFunction={this.parentClassFunction}
/>
}
}
export default parent
// Child Component
class Child extends React.Component{
onClickSubmitButton = () =>{
this.props.parentClassFunction()
};
render(){
<button onClick={this.onClickSubmitButton}>CLICK</button>
}
}
export default Child

React - Proper way to render dynamic content?

I want to make a modal view has dynamic content by injecting a component to it.
class RootView extends Component {
state = {
modalBody: null
}
setModalBody = (body) => {
this.setState({modalBody: body})
}
render() {
return(<ContextProvider value={this.setModalBody}><Modal>{this.state.modalBody}</Modal></ContextProvider>)
}
}
Then inside any children view i use setState to change parent modalBody
The modalBody can be setted on each route, which means the modalBody can be input list, selection list or text only. So the modalBody must have its state for controlling these inputs.
By this way, it renders ok, but the dynamic content couldn't be updated after state changed. The parent's dynamic content couldn't receive the ChildView new state, i have to setModalBody again and again after it rerendered.
For example, if input in modalBody has changed, the parent couldn't be updated.
class ChildView extends Component {
state = {
inputValue: null
}
handleChange = (e) => {
this.setState({inputValue: e.target.value})
}
setModalBody(body) {
this.props.context.setModalBody(<input value={this.state.inputValue} onChange={this.handleChange} />)
}
render() {
return(<Modal>{this.state.modalBody}</Modal>)
}
}
Full code: https://codesandbox.io/s/lp5p20mx1m
Any proper way to render dynamic content to parent?
I'm not sure why you'd need to create a parent Modal component, when you can make the Modal a simple reusable child component.
See here for a detailed explanation on how to achieve a stateful parent that controls a child modal.
However, if you MUST have a parent Modal component, then you can create a render prop to pass down props to be used by its children.
Working example:
components/Modal.js (parent component -- this has a lot of smaller components that were separated for reusability and ease of understanding -- they're basically simple divs with some styles attached -- see notes below)
import React, { Fragment, Component } from "react";
import PropTypes from "prop-types";
import BackgroundOverlay from "../BackgroundOverlay"; // grey background
import ClickHandler from "../ClickHandler"; // handles clicks outside of the modal
import Container from "../Container"; // contains the modal and background
import Content from "../Content"; // renders the "children" placed inside of <Modal>...</Modal>
import ModalContainer from "../ModalContainer"; // places the modal in the center of the page
class Modal extends Component {
state = { isOpen: false };
handleOpenModal = () => {
this.setState({ isOpen: true });
};
handleCloseModal = () => {
this.setState({ isOpen: false });
};
// this is a ternary operator (shorthand for "if/else" -- if cond ? then : else)
// below can be read like: if isOpen is true, then render the modal,
// else render whatever the child component is returning (in this case,
// initially returning an "Open Modal" button)
render = () =>
this.state.isOpen ? (
<Container>
<BackgroundOverlay />
<ModalContainer>
<ClickHandler
isOpen={this.state.isOpen}
closeModal={this.handleCloseModal}
>
<Content>
{this.props.children({
isOpen: this.state.isOpen,
onCloseModal: this.handleCloseModal,
onOpenModal: this.handleOpenModal
})}
</Content>
</ClickHandler>
</ModalContainer>
</Container>
) : (
<Fragment>
{this.props.children({
isOpen: this.state.isOpen,
onCloseModal: this.handleCloseModal,
onOpenModal: this.handleOpenModal
})}
</Fragment>
);
}
// these proptype declarations are to ensure that passed down props are
// consistent and are defined as expected
Modal.propTypes = {
children: PropTypes.func.isRequired // children must be a function
};
export default Modal;
components/Example.js (child component accepting isOpen, onCloseModal and onOpenModal from the parent -- with this approach, as you'll notice, there's duplicate isOpen logic. While this approach gives you full control over the parent, it's repetitive. However, you can simplify your components by moving the "Open Modal" button logic to the parent, and passing in a prop like <Modal btnTitle="Open Modal"> to make it somewhat flexible, BUT you'll still lose some control of what's being initially rendered when isOpen is false.)
import React, { Fragment } from "react";
import Modal from "../Modal";
import "./styles.css";
const Example = () => (
<div className="example">
<h2>Parent Modal Example</h2>
<Modal>
{({ isOpen, onCloseModal, onOpenModal }) =>
isOpen ? (
<Fragment>
<h1 className="title">Hello!</h1>
<p className="subtitle">There are two ways to close this modal</p>
<ul>
<li>Click outside of this modal in the grey overlay area.</li>
<li>Click the close button below.</li>
</ul>
<button
className="uk-button uk-button-danger uk-button-small"
onClick={onCloseModal}
>
Close
</button>
</Fragment>
) : (
<button
className="uk-button uk-button-primary uk-button-small"
onClick={onOpenModal}
>
Open Modal
</button>
)
}
</Modal>
</div>
);
export default Example;

Call a method from parent component via props, when clicking on a button

I'm stuck because of many multiple solutions to the problem I have, but no clear explanation for a beginner like me.
I'm building my first todo list app.
I have an App file and a ToDo child component.
From the child Todo, I'm calling the deleteTodo method, included within my parent app component, using props, but the console doesn't display any result when I click on the button.
What am I missing?
ToDo.js (full code)
import React, { Component } from 'react';
class ToDo extends Component { //define a class that extends Component
render() {
return (
<li>
<span>{ this.props.description }</span>
<button onClick ={this.props.deleteTodo}>Delete</button>
</li>
);
}
}
export default ToDo; //the component is made to export the data
App.js (for full code: https://jsfiddle.net/apjc6gk4/)
[...]
deleteTodo() {
console.log("to do deleted");
}
[...]
You need pass deleteTodo to ToDo comoponent
<ToDo key={ index }
deleteTodo={this.deleteTodo.bind(this)}
description={ todo.description }
isCompleted={ todo.isCompleted }
toggleComplete={ () => this.toggleComplete(index)}
isDeleted={todo.isDeleted}/>
You are not passing deleteTodo method as prop to Todo component
<ToDo key={ index } description={ todo.description } isCompleted={ todo.isCompleted } toggleComplete={ () => this.toggleComplete(index)} isDeleted = {todo.isDeleted}/>
Pass this method as prop to Todo component, and then call it
<ToDo key={ index } description={ todo.description } isCompleted={ todo.isCompleted } toggleComplete={ () => this.toggleComplete(index)} isDeleted = {todo.isDeleted} deleteTodo={this.deleteTodo.bind(this)}/>
In my case, following steps help me same as your case.
I hope to help you with my steps.
Define function in parent component to receive props from child component.
In parent component, pass function name to child component by props.
In child component, call props function when button clicked.
You can see result in the parent component from child.

onclick event for Imported component in react?

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 :)

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.

Resources