how i get querySelectorAll html elements in react? - javascript

I am using react with functional component. I also use react-bootstrap , I attached the react-bootstrap carousel , everything is fine with react-bootstrap, for some functionality, I want to get three divs in react , divs has same classes. I know if I use simple html css js then I easily do that,
querySelectorAll(".indicator-btn")
but i want this on react. How can I do that? please suggest me a best practices for that.
This is a my component , in the last i have three divs which i want for Dom.
import React, { useState } from "react";
import Cardcarousel from "./Cardcarousel";
import { Carousel } from "react-bootstrap";
import Brand1img from "../images/brand1.png";
import Airbnbimg from "../images/Airbnb.png";
import Microsoft from "../images/microsoft.png";
import { Row, Col } from "react-bootstrap";
function Carouselx() {
const [index, setIndex] = useState(0);
const handleSelect = (selectedIndex, e) => {
console.log(selectedIndex, "e=>", e);
setIndex(selectedIndex);
};
const isSlide = (slide) => {
console.log("slide no => " + slide);
console.log(this);
};
return (
<>
<div className="px-5 py-3">
<Carousel activeIndex={index} onSelect={handleSelect} onSlide={isSlide}>
<Carousel.Item>
<Row>
<Col>
<Cardcarousel
name="Technology UI/UX"
companylogo={Brand1img}
type="Full time job"
/>
</Col>
<Col>
<Cardcarousel
name="Technology Backend developer"
companylogo={Airbnbimg}
type="Part time job"
/>
</Col>
<Col>
<Cardcarousel
name="Technology Backend developer"
companylogo={Microsoft}
type="Internee"
/>
</Col>
</Row>
</Carousel.Item>
<Carousel.Item>
<Row>
<Col>
<Cardcarousel
name="Technology UI/UX"
companylogo={Brand1img}
type="Full time job"
/>
</Col>
<Col>
<Cardcarousel
name="Technology Backend developer"
companylogo={Airbnbimg}
type="Part time job"
/>
</Col>
<Col>
<Cardcarousel
name="Technology front end developer"
companylogo={Microsoft}
type="Internee"
/>
</Col>
</Row>
</Carousel.Item>
</Carousel>
<div className="carusel-indecator-container">
<div className="indicator-btn indicator-btn1"></div>
<div className="indicator-btn indicator-btn2"></div>
<div className="indicator-btn indicator-btn3"></div>
</div>
</div>
</>
);
}
export default Carouselx;

You could use useState to get its refs (also useRef but there'll be a lot more inconveniences potentially).
const [btn1, setBtn1] = useState();
const [btn2, setBtn2] = useState();
const [btn3, setBtn3] = useState();
...
<div ref={setBtn1} className="indicator-btn indicator-btn1"></div>
<div ref={setBtn2} className="indicator-btn indicator-btn2"></div>
<div ref={setBtn3} className="indicator-btn indicator-btn3"></div>
At some point (after 1st rendering) you'll have btn1, btn2, btn3 as button elements. If you want to use it, assumingly imperatively, you could useEffect
useEffect(() => {
if (!btn1 || !btn2 || !btn3) return;
// do something with these refs i.e. make them jquery! $(btn1)
}, [btn1, btn2, btn3]);

Related

Creating dynamic responses to button clicks in React

I've been asked to create a FAQ page with a chatbot style. The way I'm trying to do this is by creating a paragraph element with some text along the lines of "Hello, ask me a question!" with five buttons in a row underneath containing the FAQs. The idea is that the user should be able to click on one of the buttons and reveal the answer to the question in a new paragraph element and the remaining 'unanswered' questions should appear in a row of buttons below that.
So far I've only managed to create an accordion style page where the questions are in a column and the answer is revealed when they're clicked on. This works but I'd really like to try to meet my brief and get a better understanding of React at the same time. My current code is below - any help would be appreciated!
import React, { useState } from 'react';
import classes from './Chat.module.css';
import { Container, Row, Col } from 'react-bootstrap';
const ChatItem = (props) => {
const [isOpen, setIsOpen] = useState(false);
const clickHandler = () => {
setIsOpen(true);
console.log(isOpen);
};
return (
<Container>
<Row md={5}>
<button className={classes.conversation_btn} onClick={clickHandler}>
{props.question}
</button>
</Row>
<Row>
<Col mdPush={7} md={5}>
<div
className={`${classes.text_box} ${
isOpen ? classes.open : classes.closed
}`}
>
{props.answer}
</div>
</Col>
</Row>
</Container>
);
};
const ChatList = (props) => {
return (
<div>
{props.items.map((item) => (
<ChatItem
key={item.id}
id={item.id}
question={item.question}
answer={item.answer}
/>
))}
</div>
);
};
export default ChatList;

More than needed React components re-rendering when typing in input

I am taking input from a search input field using searchInput and setSearchInput useState hook and after I press submit button, I call fetchSearchData function providing it the input text and setCompanies hook, where companies are updated with the fetched list of companies from the API.
Then companies are passed to another component CompanyList where a map function is called there.
The problem is whenever I type in the search field, the CompanyList component is re-rendered although I did not press submit. I understand that setSearchInput will re-render SearchBar component whenever I type in it, but I don't get why CompanyList re-renders.
Search page source code:
const Search = () => {
const [companies, setCompanies]=useState([]); //List of companies returned from searching
const [searchInput, setSearchInput] = useState(""); //Search field input
//Update search text whenever the user types in
const onSearchChange = (e) => {
setSearchInput(e.target.value)
}
//use the API providing it the search input, and
//setCompanies hook to update list of companies
const onSearchSubmit = (e) => {
e.preventDefault()
fetchSearchData(searchInput, setCompanies)
}
return (
<div>
<Container>
<Row className={"searchFilterBar"}>
<Col sm={6} md={8} className={"searchBar"}>
<SearchBar onSubmit={onSearchSubmit} onChange={onSearchChange} value={searchInput} />
</Col>
<Col sm={6} md={4} className={"filterBar"}>
</Col>
</Row>
<CompanyList companies={companies} ></CompanyList>
<Row>
</Row>
</Container>
</div>
)
}
export default Search;
SearchBar component source code:
const SearchBar = ({value,onSubmit, onChange}) => {
return (
<Form
className="search-form"
onSubmit={onSubmit}
>
<div className="input-group">
<span className="input-group-text rubik-font">
<i className="icon ion-search"></i>
</span>
<input
className="form-control rubik-font"
type="text"
placeholder="Search for companies that start with..."
onChange={onChange}
value={value}
/>
<Button className="btn btn-light rubik-font" type="submit">Search </Button>
</div>
</Form>
)
}
CompanyList component source code:
function MapDataToCompanyList(response) {
console.log(response); //Logging occurs here
if(!response || response===undefined || response.length===0)
{
return (<ErrorBoundary message={noCompaniesError.message}></ErrorBoundary>)
}
return response.map((company) => {
return (
<Col key={company._id} xs={12} md={6} lg={4} className="mt-2">
<CompanyCard
id={company._id}
logo={company.logo}
title={company.name}
logoBackground={company.logoBackground}
progLangs={company.progLangs}
backend={company.backend}
frontend={company.frontend}
url={company.url}
>
</CompanyCard>
</Col>
)
})
}
const CompanyList = (props) => {
const {companies} = props
return (
<div>
<Container className="mt-3">
<Row>
{
MapDataToCompanyList(companies)
}
</Row>
</Container>
</div>
)
}
export default CompanyList;
FetchSearchData function source code:
export const fetchSearchData = (query, cb)=>{
const uri = process.env.NODE_ENV === 'development' ?
`http://localhost:3000/api/companies/name/${query}` :
``;
axios.get(uri, {
timeout: MAX_TIMEOUT
})
.then((response)=>{
cb(response.data.data)
})
.catch((error)=>{
console.log(error)
})
}
As seen above, empty list of companies is logged when the page first loads, then I typed three characters and the it logged three time which means the map function called three times.
Even then if I pressed submit and retrieved list of companies normally, whenever I type it will keep printing the array of companies that was fetched.
Sorry if I missed something, I am still new to React.
When you call setSearchInput(e.target.value), Search component will re-render cause its state has changed. Search component re-renders means every tag nested in it will re-render (except the ones passed via children). That is the normal behaviour of React. If you want to avoid that, you would wanna use React.memo for CompanyList. Or you could use useRef to bind the input like so:
const Search = () => {
const [companies, setCompanies] = useState([]); //List of companies returned from searching
const inputRef = React.useRef(null);
//use the API providing it the search input, and
//setCompanies hook to update list of companies
const onSearchSubmit = (e) => {
e.preventDefault();
fetchSearchData(inputRef.current.value, setCompanies);
inputRef.current.value = "";
};
return (
<div>
<Container>
<Row className={"searchFilterBar"}>
<Col sm={6} md={8} className={"searchBar"}>
<SearchBar inputRef={inputRef} onSubmit={onSearchSubmit} />
</Col>
<Col sm={6} md={4} className={"filterBar"}></Col>
</Row>
<CompanyList companies={companies}></CompanyList>
<Row></Row>
</Container>
</div>
);
};
export default Search;
const SearchBar = ({ onSubmit, inputRef }) => {
return (
<Form className="search-form" onSubmit={onSubmit}>
<div className="input-group">
<span className="input-group-text rubik-font">
<i className="icon ion-search"></i>
</span>
<input
ref={inputRef}
className="form-control rubik-font"
type="text"
placeholder="Search for companies that start with..."
/>
<Button className="btn btn-light rubik-font" type="submit">
Search
</Button>
</div>
</Form>
);
};
I don't get why CompanyList re-renders.
Because it's nested in your Search component, and it's not React.memo'd (or a PureComponent).
Yes, the component is updated, but that doesn't mean it necessarily causes a DOM reconciliation.
In any case, React is completely at liberty of calling your component function as many times as it likes (and indeed, in Strict Mode it tends to call them twice per update to make sure you're not doing silly things), so you should look at side effects (such as console logging) in your component function (which you shouldn't have in the first place) as performance guidelines.
You do not need to maintain a state for input field. You can use useRef and pass it to input like below.
<input
ref={inputRef}
className="form-control rubik-font"
type="text"
placeholder="Search for companies that start with..."
/>
And you can get get value inside onSearchSubmit using inputRef.current.value
This will not re-render you component on input change.

Create a new row every three columns

I am trying to create a virtual shop and I want to make every row of products have four items on a large screen, three in medium, and two in smell.
My problem is that I can’t come up with a way to make it that every four items I iterate the item list a new row will be created.
(I am getting the data from an API I created with Flask, the getting the data part works.)
Here is my code:
import React, { useState, useEffect } from "react";
import Container from 'react-bootstrap/Container';
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';
function ShopItems () {
const [items, setItems] = useState([]);
useEffect(() => {
fetch('/api/items').then(res => res.json()).then(data => {
setItems(data.items);
});
}, []);
return(
<Container fluid>
<Row xs={6} md={4} lg={3}>
{
items.map((item) => (
<Col key={item.id}>{item.name}</Col>))
}
</Row>
</Container>
)
}
export default ShopItems;
You are using props incorrectly.
<Container fluid>
<Row>
<Col xs={6} md={4} lg={3}></Col>
</Row>
</Container>

React: object throws ".keys is not a function Error"

I'm aware that this error may be quite common and have been answered several times before, but I couldn't find a solution.
My code always throws this error: ".map is not a function". I know that this happens because data is not an array. So I tried to solve this with .keys function but this throws the ".keys is not a function" error. I'm declaring const data in the parent component and want to use it in the child component.
I think my error depends on a false use of .keys. But after much Googling I'm still not one step further.
This is my Child-Code:
import React from "react";
import Card from 'react-bootstrap/Card';
import Col from 'react-bootstrap/Col';
import Row from 'react-bootstrap/Row';
import Container from 'react-bootstrap/Container';
import {Link} from 'react-router-dom'
const PostsRow = (data) => {
{return (
<Container>
<Row>
{data.keys(data).map((data) => {
console.log(data + "is mount")
return (
<Col className="col-6 col-md-6 col-lg-3 card">
<Link to={data.url}>
<Card className=" text-center ">
<Card.Img variant="top" src={data.imagesrc} />
<Card.Body>
<Card.Title>{data.title}</Card.Title>
</Card.Body>
</Card>
</Link>
</Col>
);
})}
</Row>
</Container>
);
};
export default PostsRow;
This is home.jsx (parent):
import React from "react";
import './route.css';
import PostsRow from "../components/Content/PostsRow";
const Home = () => {
const data = {
title: "Ersti",
imagesrc: "./490.jpg",
url: "/meineposts"
};
return (
<div>
<PostsRow data={data}/>
</div>
);
};
export default Home;
This is working fine as long as const data is declared in the PostsRow.jsx, but when I try to declare it in Home.jsx and use props the above error throws.
As data is an object. To get its keys, you should write: Object.keys(data).
And, you have a typo in props destructuring : it should be ({data}).
Your example data is simply an object, not an array, so you don't need to use map or Object.keys here, you can simply write:
<Col className="col-6 col-md-6 col-lg-3 card">
<Link to={data.url}>
<Card className="text-center">
<Card.Img variant="top" src={data.imagesrc} />
<Card.Body>
<Card.Title>{data.title}</Card.Title>
</Card.Body>
</Card>
</Link>
</Col>
PostsRow will be called with props and data is property of it. so you have to use it like
const PostsRow = ({data}) => {
And you've to convert your data to array like
const data = [{
title: "Ersti",
imagesrc: "./490.jpg",
url: "/meineposts"
}];

React Display component after click on card

I creating simple app. It must be app with information about pokemons. So I need to create, when user click on pokeCard, Sidebar info.
How it look now:
So, Sidebar is must to be, it can be, for example, only white background.
I think about styled-components, but I not sure, that this would be the right decision
How to do it with functional Component?
Wrapper
const [SelectedPokemonIndex, setSelectedPokemonIndex] = useState();
return (
<Row>
<Col xs={24} sm={14} lg={16}>
<Pokemons
PokemonsList={PokemonsList}
loadMoreItems={loadMoreItems}
Loading={Loading}
onClickPoke={(pokemonId) => {
fetchPokemonDetails(pokemonId);
fetchPokemon(pokemonId);
fetchPokemonStats(pokemonId);
setSelectedPokemonIndex(pokemonId);
}}
/>
</Col>
<Col xs={24} sm={10} lg={8}>
<About
pokemon={SelectedPokemon}
PokemonTypes={PokemonTypes}
PokemonStats={PokemonStats}
index={SelectedPokemonIndex}
LoadingForSelectedPokemon={LoadingForSelectedPokemon}
/>
</Col>
</Row>
);
}
export default Wrapper;
Child component of wrapper
function Pokemons(props) {
let { PokemonsList, loadMoreItems, Loading, onClickPoke } = props;
return (
<GridCard
image={`${IMAGE_BASE_URL}${++index}.png`}
pokemonId={index}
pokemonName={pokemon.name}
pokemonUrl={pokemon.url}
onClickPoke={onClickPoke}
/>
PokeCard
import React, { useEffect, useState } from "react";
import { Col, Typography } from "antd";
import "./GridCards.css";
const { Title } = Typography;
function GridCards(props) {
let { key, image, pokemonName, pokemonUrl, pokemonId } = props;
return (
<Col
key={key}
lg={8}
md={12}
xs={24}
onClick={() => {
props.onClickPoke(pokemonId);
}}
>
<div
className="poke-card"
}}
>
<img alt={pokemonName} src={image} />
{LoadingForPokemon && <div>Loading...</div>}
</div>
</Col>
);
}
export default GridCards;
This is Sidebar, what must to be change:
function About(props) {
let {
pokemon,
LoadingForSelectedPokemon,
index,
PokemonTypes,
PokemonStats,
} = props;
return (
<div
style={{
position: "sticky",
top: 0,
display: "flex",
justifyContent: "center",
}}
>
<PokemonDetails
pokemonName={pokemon.name}
pokemonId={pokemon.id}
pokemon={pokemon}
LoadingForSelectedPokemon={LoadingForSelectedPokemon}
image={`${IMAGE_BASE_URL}${index}.png`}
PokemonTypes={PokemonTypes}
PokemonStats={PokemonStats}
/>
</div>
);
}
This is a pretty broad question and there's a lot going on in your code, but I think you need to move some state management around.
In your GridCards component, give it a prop called onCardClick and call that function in the onClick of the <Col> component you're using. It'll look something like this:
function GridCard(props) {
const { key, image, pokemonName, pokemonUrl, pokemonId, onCardClick } = props;
return (
<Col
key={key}
lg={8}
md={12}
xs={24}
onClick={() => onCardClick()}
>
<div
className="poke-card"
>
<img alt={pokemonName} src={image} />
{LoadingForPokemon && <div>Loading...</div>}
</div>
</Col>
);
}
export default GridCard;
Then in your wrapper components, instead of using the Pokemons component, I think you can just use your GridCard component and map on whatever data you're using to render out the cards currently.
So something like this:
export default function Wrapper() {
const [selectedPokemon, setSelectedPokemon] = useState(null);
// data is whatever you have to iterate over to put data into your cards
const pokemonCards = data.map((p, idx) => {
return (
<GridCard
key={idx}
image={p.image}
pokemonName={p.pokemonName}
onCardClick={() => setSelectedPokemon(p)}
></GridCard>
);
});
return (
<Row>
<Col xs={24} sm={14} lg={16}>
{pokemonCards}
</Col>
<Col xs={24} sm={10} lg={8}>
<About pokemon={selectedPokemon} />
</Col>
</Row>
);
}
Basically, what you should try and accomplish is letting each GridCard component contain all the necessary information for your About component, so that when you click on a GridCard your Wrapper's state can update with a selectedPokemon and then pass that back down into your About component.
I found solution for my question.
Its only need to set isAboutShown
const [isAboutShown, setAboutShow] = useState(false);
And onClick I coding setAboutShow(true)
How to display component?
{isAboutShown && (
<About/>)

Categories

Resources