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;
Related
I'm having a problem that I can't solve. I have a component that is currently rendering the users that are in my database, which calls CarouselUsers.jsx, so far so good, it is showing correctly.
But my goal is that after I click on one of these users that were listed, his name appears in a sidebar, which is in another component, but I am not able to do that, can you help me?
CarouselUsers.jsx
import React, { useState, useEffect } from 'react';
import * as Styled from './style.jsx';
import {
collection,
getDocs,
} from "firebase/firestore";
import { Swiper, SwiperSlide } from "swiper/react";
import { db } from '../../Data/Firebase.jsx';
import "swiper/css";
import euTeste from '../../assets/teste.jfif'
import SideBarProfile from '../../components/SideBarProfile/SideBarProfile.jsx';
export default function CarouselUsers() {
const [profile, setProfile] = useState(false)
const openProfile = () => {
setProfile(profile => !profile)
}
// USERS IN THE DB
const [users, setUsers] = useState([])
const usersCollectionRef = collection(db, "usuarios")
useEffect(() => {
const getUsers = async () => {
const data = await getDocs(usersCollectionRef);
setUsers(data.docs.map((doc) => ({ ...doc.data(), id: doc.id })));
};
getUsers();
}, []);
// USERS IN THE DB
return (
<>
<Styled.CarouselUsers>
{/* MEMBROS CARROSEL */}
<div className="boxMembros">
<div className="titulo">
<h6>Membros</h6>
</div>
<Swiper
spaceBetween={10}
slidesPerView={3}
>
{users.map((user) => {
return (
<>
<SwiperSlide>
<div className="box"style={{ background: `linear-gradient(to bottom,rgba(0, 0, 0, 0.4) 0,rgba(0,0,0,.6) 100%),url(${euTeste})` }} onClick={openProfile} key={user.nome}>
<div className="infoBottom">
<div className="info">
{/* GET THE USERNAME */}
<h6>{user.nome}</h6>
{/* GET THE USERNAME */}
</div>
</div>
</div>
</SwiperSlide>
</>
);
})}
</Swiper>
</div>
{/* MEMBROS CARROSEL */}
</Styled.CarouselUsers>
<SideBarProfile profile={profile} openProfile={openProfile} />
</>
)
}
SideBarProfile.jsx
import React from 'react'
import { XCircle,WhatsappLogo } from "phosphor-react";
import * as Styled from './style.jsx';
export default function SideBarProfile({openProfile,profile}) {
return (
<Styled.SideBarProfile>
<div className={profile ? 'col-md-3 boxLeftWrapper open' : 'col-md-3 boxLeftWrapper close'} profile={profile}>
<div className="boxAll">
<div className="header d-flex justify-between align-items-center">
<div className="titulo">
<h1>Perfil</h1>
</div>
<div className="close">
<button onClick={openProfile}>
<XCircle/>
</button>
</div>
</div>
<div className="boxBodyUser text-left">
<div className="boxThis">
<div className="foto">
<img alt="Usuário" className='img-fluid ativo' />
</div>
<div className="nome text-center">
<h5>{/* SHOW USERNAME */}</h5>
</div>
<div className="status ativo">
<span>Ativo</span>
</div>
<div className="ministerios">
<ul className="pl-0 list-none mb-0">
<li>Teatro</li>
<li>Mídias Sociais</li>
</ul>
</div>
<div className="boxContato mt-5">
<div className="whatsapp d-flex items-center justify-center gap-2">
<WhatsappLogo/>
<span>Mensagem</span>
</div>
</div>
</div>
</div>
</div>
</div>
</Styled.SideBarProfile>
)
}
You can add an onClick event in your CarouselUsers component that grab the Inner Text in <h6>{user.nome}</h6> and pass it as props to SideBarProfile component .
like this :
CarouselUsers.jsx :
export default function CarouselUsers() {
const [profile, setProfile] = useState(false)
const [selectedUser, setSelectedUser] = useState("")
const handleClick = (event) => {
setSelectedUser(event.target.innerText);
}
// rest of your code
return (
......
{/* GET THE USERNAME */}
<h6 onClick={handleClick} >{user.nome}</h6>
{/* GET THE USERNAME */}
.... rest of your code
<SideBarProfile profile={profile} openProfile={openProfile}
setSelectedUser = {setSelectedUser} />
)
}
SideBarProfile.jsx :
export default function SideBarProfile({openProfile,profile, setSelectedUser}) {
return (
......
<div className="nome text-center">
<h5>{setSelectedUser}</h5>
</div>
....
)
I'm pretty new to react, and I am trying to make an Increment and decrement component in in my product screen. The issue that I'm having is that I would like it to only be able to increment if the there are that many items in stock, but when I try to do this the component stops showing the number associated with amount of the item chosen. I would really appreciate any help or guidance on how to do this.
Thank you!
ProductScreen.js
import React, { useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
import { detailsProduct } from '../actions/productActions';
import LoadingBox from '../components/LoadingBox';
import MessageBox from '../components/MessageBox';
import { IconContext } from 'react-icons';
import { FiPlus, FiMinus } from 'react-icons/fi';
export default function ProductScreen(props) {
const dispatch = useDispatch();
const productId = props.match.params.id;
const [qty, setQty] = useState(1);
const productDetails = useSelector((state) => state.productDetails);
const { loading, error, product } = productDetails;
const handleQuantity = (type) => {
if (type === "dec") {
qty > 1 && setQty(qty - 1);
}else if(product.countInStock >= product.qty){
setQty(qty + 1);
}
};
return (
<div>
{loading ? (
<LoadingBox></LoadingBox>
) : error ? (
<MessageBox variant="danger">{error}</MessageBox>
) : (
<div>
<Link to="/body">Back to result</Link>
<div className="row top">
<div className="col-1">
<div className="card card-body">
<ul>
<li>
<div className="row">
<div>Status</div>
<div>
{product.countInStock > 0 ? (
<span className="success">In Stock</span>
) : (
<span className="danger">Unavailable</span>
)}
</div>
</div>
</li>
{product.countInStock > 0 && (
<>
<li>
<div className="row">
<div>Qty</div>
<div className="AddContainer">
<div className="AmountContainer">
<FiMinus onClick={() => handleQuantity("dec")}/>
<div className="Amount">{product.qty}</div>
<FiPlus onClick={() => handleQuantity("inc")}/>
</div>
</div>
<div>
</div>
</div>
</li>
</>
)}
</ul>
</div>
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
);
}
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;
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
I am using react/redux & would like to filter through posts that are from my backend in Node. I am mapping through the posts and they are being shown. Trying to implement a filter input I am looking for some advice on how to use react hooks to get this working.
My component...
import React, { Fragment, useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import Spinner from '../layout/Spinner';
import { getCommunityPosts } from '../../actions/communitypost';
import CommunityPostItem from './CommunityPostItem';
import Cooking from '../../img/vegetables.jpg';
import Lifestyle from '../../img/lifestyle.jpg';
import General from '../../img/general.jpg';
// Lazy load
import { LazyLoadImage } from 'react-lazy-load-image-component';
import 'react-lazy-load-image-component/src/effects/blur.css';
const CommunityPosts = ({
getCommunityPosts,
communitypost: { posts, loading }
}) => {
const [searchTerm, setSearchTerm] = useState("");
const [searchPosts, setSearchPosts] = useState([]);
const handleChange = e => {
setSearchTerm(e.target.value);
}
useEffect(() => {
const results = posts.filter(post => {
console.log(post);
post.toLowerCase().includes(searchTerm)
}
);
setSearchPosts(results)
getCommunityPosts();
}, [getCommunityPosts, searchTerm]);
return (
loading ? (
<Spinner />
) : (
<Fragment>
<Fragment>
<div className="search-recipes">
<input
className="bg-white focus:outline-none focus:shadow-outline border border-gray-300 rounded-lg py-2 px-4 block w-full appearance-none leading-normal"
type="text"
value={searchTerm}
onChange={handleChange}
placeholder="Search community posts..."
></input>
</div>
<div className="pt-1 grid lg:grid-cols-3 md:grid-cols-2 sm:grid-cols-1 gap-1">
<div className="cursor-pointer category-overlay">
<Fragment>
<Link to="/communityposts/category/cooking">
<LazyLoadImage effect="blur" src={Cooking}></LazyLoadImage>
<div className="content">
<div className="recipe-category-text pacifico">Cooking</div>
</div>
</Link>
</Fragment>
</div>
<div className="cursor-pointer category-overlay">
<Fragment>
<Link to="/communityposts/category/lifestyle">
<LazyLoadImage effect="blur" src={Lifestyle}></LazyLoadImage>
<div className="content">
<div className="recipe-category-text pacifico">Lifestyle</div>
</div>
</Link>
</Fragment>
</div>
<div className="cursor-pointer category-overlay">
<Fragment>
<Link to="/communityposts/category/general">
<LazyLoadImage effect="blur" src={General}></LazyLoadImage>
<div className="content">
<div className="recipe-category-text pacifico">General</div>
</div>
</Link>
</Fragment>
</div>
</div>
</Fragment>
<Fragment>
<div className="community-posts bg-gray-200">
{posts.map(post => (
<CommunityPostItem key={post._id} post={post} />
))}
</div>
</Fragment>
</Fragment>
)
);
};
CommunityPosts.propTypes = {
getCommunityPosts: PropTypes.func.isRequired,
communitypost: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
communitypost: state.communitypost
});
export default connect(mapStateToProps, { getCommunityPosts })(CommunityPosts);
Error i'm getting is that "TypeError: post.toLowerCase is not a function"