How to get clicked element datas inside React Bootstrap Modal? - javascript

I have a problem with getting the current clicked element's data's from the API and displaying it inside React Bootstrap Modal body. The parent component is TopMovies.js which looks like:
import React, { useState } from 'react';
import TopMoviesModal from '../top-movies-modal/TopMoviesModal';
const TopMovies = ({ currentMovies }) => {
const [isOpen, setIsOpen] = useState(false);
const showModal = () => {
setIsOpen(true);
};
const hideModal = () => {
setIsOpen(false);
};
const renderMovies = () => {
if (Object.keys(currentMovies).length && currentMovies.length) {
return (
<div className="movies-labels-container">
{currentMovies.map((movie) => {
return (
<div className="movie-label"
key={movie.id.attributes['im:id']} title={movie['im:name']['label']}
onClick={showModal}>
<img className="movie-img" src={movie['im:image'][2]['label']} alt={movie['im:name']['label']} />
<br />
<div className="text-under">
<span id="movie-title">{movie['im:name']['label']}</span>
<br />
<span id="movie-subtitle">{movie.category.attributes.term}</span>
</div>
</div>
);
})}
</div>
);
}
};
return (
<section className="top-movies-list">
<div className="container-outter">
<div className="container-fluid">
<div className="row">
<div className="col-lg-12 col-md-12 col-sm-12" id="top-movies-module">
<div className="top-movies-container">
<h1 className="container-title">
TOP 100 movies
</h1>
{renderMovies()}
<TopMoviesModal
currentMovies={currentMovies}
hideModal={hideModal}
isOpen={isOpen} />
</div>
</div>
</div>
</div>
</div>
</section >
)}
export default TopMovies;
and the child component TopMoviesModal.js which looks like this:
import React from 'react';
import Modal from "react-bootstrap/Modal";
import Button from "react-bootstrap/Button";
const TopMoviesModal = ({ currentMovies, hideModal, isOpen }) => {
const renderMoviesDetails = () => {
if (Object.keys(currentMovies).length && currentMovies.length) {
return (
<div>
{currentMovies.map((movie, index) => {
return (
<div className="movie-label" key={movie.id.attributes['im:id']}>
<span id="movie-title">{movie['im:name']['label']}</span>
</div>
);
})}
</div>
);
}
};
return (
<Modal
show={isOpen}
onHide={hideModal}
size="lg"
aria-labelledby="contained-modal-title-vcenter"
centered
>
<Modal.Header closeButton>
<Modal.Title id="contained-modal-title-vcenter">
Modal heading
</Modal.Title>
</Modal.Header>
<Modal.Body>
{renderMoviesDetails()}
</Modal.Body>
<Modal.Footer>
<Button onClick={hideModal}>Close</Button>
</Modal.Footer>
</Modal>
);}
export default TopMoviesModal;
Could you help me to solve this problem? I would be very grateful, thanks a lot

Related

how to create the input div on clicking the reply button of a comment below that particular comment using react

I want on clicking the reply button the div to write the reply to come
I tried to create a seperate component for reply textarea but as I am a beginner in react I am not able to get an idea how to do it.
Comment.js
import Userdata from "./data.json";
import Score from "./Score";
import Person from "./person";
import Reply from "./Reply";
const Comment = () => {
return (<div className="sample">{
Userdata.comments.map((comment, index) => {
return (
<div key={comment.id}>
<div className="comment">
<Score score={comment.score} />
<div>
<Person user={comment.user} />
<span className="time">{comment.createdAt}</span>
<Reply index={index} />
<p>{comment.content}</p>
</div>
</div>
{comment.replies.map((reply) => {
return (
<div key={reply.id}>
<div className="comment reply">
<Score score={reply.score} />
<div>
<Person user={reply.user} />
<span>{reply.createdAt}</span>
<Reply />
<p>{reply.content}</p>
</div>
</div>
</div>
);
})}
</div>
);
})
}
</div>
);
}
export default Comment;
Reply Component
const Reply = (props) => {
const handleClick = () => {
console.log(`reply of ${props.index}`)
}
return (
<div onClick= {handleClick} className="btn-reply">
<img className="reply-icon" src="images/icon-reply.svg" alt="reply" />
<span>Reply</span>
</div>
);
}
export default Reply;

How to access function component's state variables in ReactJS?

I've been developing a website and I'm faced with an issue about accessing state varibles to a component. I've a address page(Addresses.jsx file) which has address modal. I'm also have a component which contains address modal's content (Address.jsx). I would like to access state inside Address.jsx file. How can I do that? If you can help me. I would be very appreciate.
Here is my code blocks;
Address.jsx file;
const Addresses = () => {
const [selectedAddress, setSelectedAddress] = useState();
const [modalType, setModalType] = useState();
const {
elementRef: addressModalRef,
show: showAddressModal,
hide: hideAddressModal,
} = useModal();
return (
<>
<div className="bg-light px-3 px-lg-4 py-4">
<h1 className="h4 mb-4">My Addresses</h1>
<div className="row g-3">
<div className="col-12 col-md-6 col-lg-4">
<AddressButton
onClick={() => {
setSelectedAddress(undefined);
setModalType("NewAddress");
showAddressModal();
}}
/>
</div>
{ADDRESSES.map((address, i) => (
<div className="col-12 col-md-6 col-lg-4" key={i}>
<AddressButton
{...address}
onClick={() => {
setSelectedAddress(address);
setModalType("EditAddress");
showAddressModal();
}}
/>
</div>
))}
</div>
</div>
<AddressModal
address={selectedAddress}
elementRef={addressModalRef}
hide={hideAddressModal}
modalType={modalType}
/>
</>)};
export default Addresses;
Address Modal file;
const AddressModal = ({ address, elementRef, hide, modalType }) => {
const onSaveAddress = () => {
console.log(address);
hide();
};
return (
<Modal elementRef={elementRef} centered size="lg" fullscreen>
<ModalBody>
<AddressForm address={address} /> // I would like to get this component's state in onSaveAddress funtion
</ModalBody>
<ModalFooter>
<button
type="button"
className="btn btn-primary flex-grow-1 px-5"
onClick={onSaveAddress}>
Kaydet
</button>
</ModalFooter>
</Modal>)};
export default AddressModal;
Instead of accessing child components state, which is not possible, do the following
Address Model
<AddressForm
address={address}
onSave={onSaveAddress}
/>
Address Form
const AddressForm = ({ onSave }) => {
const handleFormSubmit = () => {
onSave(formData);
};
return (
<form onSubmit={handleFormSubmit}>
</form>
);
}
Then in onSaveAddress, you will have access to the form data.

Show/Hide Component by passing useState Value React

I'm building a website with React and Bootstrap which would have a static footer. The footer will contain two buttons - Back, and Next. Clicking 'back' decrements an index, and clicking 'next' would increment the index. Ideally this index would keep track of which js component to show or hide using a ternary statement with with display: 'inline' or 'none'. I've tried useState in the App.js file, AND in the Footer.js file, but I am unable to pass the useState values between components it seems. Is there a better way to do this? I have provided some of my code which does not work.
Footer.js:
import React from "react";
import { useState } from "react";
const Footer = (props) => {
const [pageIndex, setPageIndex] = useState(0);
return (
<div className="container-lg">
<div class="row justify-content-between mt-5">
<button
onClick={setPageIndex(pageIndex - 1)}
>
Back
</button>
<button
onClick={setPageIndex(pageIndex + 1)}
>
Next
</button>
</div>
</div>
);
};
export default Footer;
App.js:
function App() {
return (
<div className="App">
<div style={{display: Footer.pageIndex === 0 ? 'inline' : 'none'}}>
<Component />
</div>
<div style={{display: Footer.pageIndex === 1 ? 'inline' : 'none'}}>
<AnotherComponent />
</div>
<Footer />
</div>
);
}
export default App;
Few issues with your code: 1) className, not the class in react. 2) Error in your onClick parameters. 3) You need to move state up to the App component.
const {useState} = React;
function App() {
const [pageIndex, setPageIndex] = useState(0);
return (
<div className="App">
<div style={{ display: pageIndex === 0 ? "inline" : "none" }}>
<Component />
</div>
<div style={{ display: pageIndex === 1 ? "inline" : "none" }}>
<AnotherComponent />
</div>
<Footer setPageIndex={setPageIndex} />
</div>
);
}
const Footer = ({ setPageIndex }) => {
return (
<div className="container-lg">
<div className="row justify-content-between mt-5">
<button onClick={() => setPageIndex((prev) => prev - 1)}>Back</button>
<button onClick={() => setPageIndex((prev) => prev + 1)}>Next</button>
</div>
</div>
);
};
const Component = (props) => {
return <p>Component</p>;
};
const AnotherComponent = (props) => {
return <p>AnotherComponent</p>;
};
ReactDOM.createRoot(
document.getElementById("root")
).render(
<App />
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
<div id="root"></div>
Shift the state of the footer to be at the parent which is App.js
import React from "react";
import { useState } from "react";
const Footer = (props) => {
const { setPageIndex, pageIndex } = props;
return (
<div className="container-lg">
<div class="row justify-content-between mt-5">
<button onClick={()=> setPageIndex(pageIndex - 1)}>Back</button>
<button onClick={()=> setPageIndex(pageIndex + 1)}>Next</button>
</div>
</div>
);
};
export default Footer;
and then you could pass the setPageIndex as prop to the footer.
function App() {
const [pageIndex, setPageIndex] = useState(0);
return (
<div className="App">
<div style={{ display: Footer.pageIndex === 0 ? "inline" : "none" }}>
<Component />
</div>
<div style={{ display: Footer.pageIndex === 1 ? "inline" : "none" }}>
<AnotherComponent />
</div>
<Footer setPageIndex={setPageIndex} pageIndex={pageIndex} />
</div>
);
}
export default App;

Why does a useCallback option have to be called with a class component in Reactjs?

I am stumped. I have been trying to get a simple cleartext button to work. I have tried all the option available on this platform but nothing is working for me. React Hook useCallback cannot be called in a class component. React Hooks must be called in a React function component or a custom React Hook. I have no idea why this error is occurring. I am pretty new to react can anyone please help? I am trying to clear the text on a click of the clear button.
import React, { Component, useCallback, useState } from "react";
import {
Button,
Input,
Footer,
Card,
CardBody,
CardImage,
CardTitle,
CardText
} from "mdbreact";
import blankImg from "./blank.gif";
import "./style.css";
import "./flags.min.css";
import countriesList from "./countries.json";
class App extends Component {
state = {
search: ""
};
renderCountry = country => {
const { search } = this.state;
var code = country.code.toLowerCase();
const handleClick = useCallback(() => {
e.target.value = '';
}, []);
return (
<div className="col-md-3" style={{ marginTop: "20px" }}>
<Card>
<CardBody>
<p className="">
<img
src={blankImg}
className={"flag flag-" + code}
alt={country.name}
/>
</p>
<CardTitle title={country.name}>
{country.name.substring(0, 15)}
{country.name.length > 15 && "..."}
</CardTitle>
</CardBody>
</Card>
</div>
);
};
onchange = e => {
this.setState({ search: e.target.value });
};
render() {
const { search } = this.state;
const filteredCountries = countriesList.filter(country => {
return country.name.toLowerCase().indexOf(search.toLowerCase()) !== -1;
});
return (
<div className="flyout">
<main style={{ marginTop: "4rem" }}>
<div className="container">
<div className="row">
<div className="col">
<Input
label="Search Country"
icon="search"
onChange={this.onchange}
/>
<button onClick={handleClick}> Click to clear</button>
</div>
<div className="col" />
</div>
<div className="row">
{filteredCountries.map(country => {
return this.renderCountry(country);
})}
</div>
</div>
</main>
<Footer color="indigo">
<p className="footer-copyright mb-0">
© {new Date().getFullYear()} Copyright
</p>
</Footer>
</div>
);
}
}
export default App;
It is because you try to use hooks in a class based component. You can only use hooks like useCallback in functional components. Therefore you are mixing the concepts of object oriented and functional programming.
The following should do the trick:
import React, { Component, useCallback, useState } from "react";
import {
Button,
Input,
Footer,
Card,
CardBody,
CardImage,
CardTitle,
CardText
} from "mdbreact";
import blankImg from "./blank.gif";
import "./style.css";
import "./flags.min.css";
import countriesList from "./countries.json";
class App extends Component {
state = {
search: ""
};
const handleClick = (e) => {
e.target.value = '';
};
renderCountry = country => {
const { search } = this.state;
var code = country.code.toLowerCase();
return (
<div className="col-md-3" style={{ marginTop: "20px" }}>
<Card>
<CardBody>
<p className="">
<img
src={blankImg}
className={"flag flag-" + code}
alt={country.name}
/>
</p>
<CardTitle title={country.name}>
{country.name.substring(0, 15)}
{country.name.length > 15 && "..."}
</CardTitle>
</CardBody>
</Card>
</div>
);
};
onchange = e => {
this.setState({ search: e.target.value });
};
render() {
const { search } = this.state;
const filteredCountries = countriesList.filter(country => {
return country.name.toLowerCase().indexOf(search.toLowerCase()) !== -1;
});
return (
<div className="flyout">
<main style={{ marginTop: "4rem" }}>
<div className="container">
<div className="row">
<div className="col">
<Input
label="Search Country"
icon="search"
onChange={this.onchange}
/>
<button onClick={this.handleClick}> Click to clear</button>
</div>
<div className="col" />
</div>
<div className="row">
{filteredCountries.map(country => {
return this.renderCountry(country);
})}
</div>
</div>
</main>
<Footer color="indigo">
<p className="footer-copyright mb-0">
© {new Date().getFullYear()} Copyright
</p>
</Footer>
</div>
);
}
}
export default App;
useCallback hooks can only be used in functional components OR in a custom Hook NOT in class components.
You have declared your App component in class based approach i.e. Object oriented approach. To use useCallback hook for this, you should change your App component declaration to a functional component or Custom hook.
Something like this
function App() {
return (
<>Hello World!</>
);
}
you cannot useCallBack hook in class Component,we can do a simple thing to clear the button text, by using
a button text state, i.e. { btnTxt: "Clear ButtonText"} than update the state onclick
import React, { Component, useCallback, useState } from "react";
import {
Button,
Input,
Footer,
Card,
CardBody,
CardImage,
CardTitle,
CardText
} from "mdbreact";
import blankImg from "./blank.gif";
import "./style.css";
import "./flags.min.css";
import countriesList from "./countries.json";
class App extends Component {
state = {
search: "",
btnTxt: "Clear ButtonText"
};
renderCountry = country => {
const { search } = this.state;
var code = country.code.toLowerCase();
return (
<div className="col-md-3" style={{ marginTop: "20px" }}>
<Card>
<CardBody>
<p className="">
<img
src={blankImg}
className={"flag flag-" + code}
alt={country.name}
/>
</p>
<CardTitle title={country.name}>
{country.name.substring(0, 15)}
{country.name.length > 15 && "..."}
</CardTitle>
</CardBody>
</Card>
</div>
);
};
onchange = e => {
this.setState({ search: e.target.value });
};
render() {
const { search } = this.state;
const filteredCountries = countriesList.filter(country => {
return country.name.toLowerCase().indexOf(search.toLowerCase()) !== -1;
});
return (
<div className="flyout">
<main style={{ marginTop: "4rem" }}>
<div className="container">
<div className="row">
<div className="col">
<Input
label="Search Country"
icon="search"
onChange={this.onchange}
/>
<button onClick={()=> this.setState({btnText:""})}> {this.state.btnTExt}</button>
</div>
<div className="col" />
</div>
<div className="row">
{filteredCountries.map(country => {
return this.renderCountry(country);
})}
</div>
</div>
</main>
<Footer color="indigo">
<p className="footer-copyright mb-0">
© {new Date().getFullYear()} Copyright
</p>
</Footer>
</div>
);
}
}
export default App;

React-Redux: Passing Event Handling inside map function

New to React/Redux, I am having hard time implementing on event handling.
I know that the 'this' reference key goes null when passed into the map (this.props.addRecipe.map of recipebox) function but I don't how to resolve it.
Essentially I would like to pass the onChange handler to ModalBox for each element in the array.
src/containers/recipebox
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { ListGroup, ListGroupItem, Panel, Button, Modals } from 'react-bootstrap';
import MyModal from '../components/mymodal';
import { bindActionCreators } from 'redux';
import { deleteRecipe } from '../actions/index';
import shortid from 'shortid'
import ModalBox from '../containers/modalbox'
class RecipeBox extends Component {
constructor(props){
super(props);
this.renderRecipeList = this.renderRecipeList.bind(this)
this.handleRecipeNameChange = this.handleRecipeNameChange.bind(this)
this.handleUserIngredientsChange = this.handleUserIngredientsChange.bind(this)
}
handleRecipeNameChange(event){
this.setState({recipeName: event.target.value})
}
handleUserIngredientsChange(event){
this.setState({userIngredients: event.target.value})
}
renderRecipeList(recipeItem, index){
const recipe = recipeItem.recipe;
const ingredients = recipeItem.ingredients;
const id = shortid.generate();
return(
<div key={id}>
<Panel bsStyle="primary" collapsible header={<h3>{recipe}</h3>}>
<ListGroup >
<ListGroupItem header="Ingredients"></ListGroupItem>
{ingredients.map(function(ingredient,index){
return <ListGroupItem key={index}>{ingredient}</ListGroupItem>;
})}
<ListGroupItem>
<Button
onClick={() => this.props.deleteRecipe(recipeItem)}
bsStyle="danger">Delete
</Button>
<ModalBox
modalTextTitle={'Edit Recipe'}
recipeName={recipe}
userIngredients={ingredients}
handleRecipeNameChange={this.handleRecipeNameChange}
handleUserIngredientsChange={this.handleUserIngredientsChange}
onClickSubmit={this.onClickSubmit}
/>
</ListGroupItem>
</ListGroup>
</Panel>
</div>
)
}
render(){
return(
<div className="container">
<div className='panel-group'>
{this.props.addRecipe.map(this.renderRecipeList)}
</div>
</div>
)
}
}
function mapStateToProps(state) {
return {
addRecipe : state.recipeState
};
}
function mapDispatchToProps(dispatch){
return bindActionCreators({deleteRecipe : deleteRecipe}, dispatch)
}
export default connect(mapStateToProps,mapDispatchToProps)(RecipeBox);
src/containers/modalbox
import React, { Component } from 'react';
import { Button, Modal } from 'react-bootstrap';
class ModalBox extends Component {
constructor(props){
super(props)
this.state = {
showModal: false
};
this.toggleModal = this.toggleModal.bind(this);
}
toggleModal(){
this.setState({
showModal: !this.state.showModal
});
}
submitData(link){
link()
this.toggleModal()
}
render() {
return (
<div>
<Button
bsStyle="info"
onClick={this.toggleModal}
>
{this.props.modalTextTitle}
</Button>
<Modal show={this.state.showModal} onHide={this.toggleModal}>
<Modal.Header closeButton>
<Modal.Title>{this.props.modalTextTitle}</Modal.Title>
</Modal.Header>
<Modal.Body>
<form>
<div className="form-group">
<label htmlFor="recipeName">Name of Recipe:</label>
<input
value={this.props.recipeName}
onChange= {this.props.handleRecipeNameChange}
type="text"
className="form-control"
id="recipeName" />
</div>
<div className="form-group">
<label htmlFor="userIngredients">Ingredients:</label>
<textarea
placeholder="you can seperate by comma"
value={this.props.userIngredients}
onChange={this.props.handleUserIngredientsChange}
type="text"
className="form-control"
id="userIngredients" />
</div>
</form>
</Modal.Body>
<Modal.Footer>
<Button
bsStyle="info"
onClick={ () => this.submitData(this.props.onClickSubmit) }>
{this.props.modalTextTitle}
</Button>
<Button
bsStyle="danger"
onClick= {this.toggleModal}
>Close</Button>
</Modal.Footer>
</Modal>
</div>
);
}
}
export default ModalBox
inside map function you need to change the this like below code,
render(){
const self = this;
return(
<div className="container">
<div className='panel-group'>
{this.props.addRecipe.map(self.renderRecipeList)}
</div>
</div>
)
}

Categories