Matching two divs height on resize in React - reactjs

I have this
const divRef = createRef()
const [dimensions, setDimensions] = useState({ width: 1, height: 2 })
const useRefDimensions = (ref) => {
useEffect(() => {
if (ref.current) {
const { current } = ref
const boundingRect = current.getBoundingClientRect()
const { width, height } = boundingRect
setDimensions({ width: Math.round(width), height: Math.round(height) })
}
}, [ref])
return dimensions
}
useRefDimensions(divRef)
On the render...
<Row>
<Col md={12} lg={7}>
<Card bg="dark" ref={divRef}>
Some image or video here
</Card>
</Col>
<Col md={12} lg={5}>
<PerfectScrollbar style={{height: dimensions.height}} >
<Card bg="dark">
Some stuff in here
</Card>
</PerfectScrollbar>
</Col>
</Row>
I can make it work (sort of) but only if I use the ref dependency. Problem is, I get an error and what it looks like an infinite loop
index.js:1 Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
EDIT - The entire component
import React, {useState, useEffect, useRef} from 'react'
import { Card, Row, Col } from 'react-bootstrap'
import PerfectScrollbar from 'react-perfect-scrollbar'
import succession from '../../assets/images/succession/succession.jpeg'
import successiontrailerposter from '../../assets/images/succession/succesion trailer.jpg'
import successiontrailer from '../../assets/images/succession/successionTrailer.mp4'
import {ReactVideo} from 'reactjs-media'
const metadata = [
{
title: "Succession",
source: "HBO",
type: "Original",
mediatype: "TV Series",
rating: "TV-MA",
seasons: 3,
episodes: 30,
poster: succession,
trailerposter: successiontrailerposter,
trailer: successiontrailer,
summary: "When Logan Roy (Brian Cox) announces his plans to step back from running the family's multi-national entertainment company, a feud over who will be have control causes tension among his four children.",
info: [
{
releasedate: "June 3, 2018",
availabilitydate: "May 27, 2020",
runtime: "56 - 70 minutes",
genres:[ "Drama", "Comedy-Drama", "Black-Comedy" ],
tags: ["Patriarchy", "finance", "based on true story" ],
regions: ["North America", "Latin America"],
language: "English",
awards: ["Golden Globe: Best TV Series"],
prod_budget: "$50,000,000",
marketing_budget: "$3,000,000",
}
]
}
]
const Home = () => {
const divRef = useRef()
const [dimensions, setDimensions] = useState({ width: 1, height: 2 })
useEffect((ref) => {
if (ref.current) {
const { current } = ref
const boundingRect = current.getBoundingClientRect()
const { width, height } = boundingRect
setDimensions({ width: Math.round(width), height: Math.round(height) + 35 })
}
return dimensions
}, [dimensions])
return (
<div className="container-fluid pt-3 px-5">
{metadata.map((item, index) => {
return (
<div key={index}>
<div className="d-flex align-items-end justify-content-between pb-4">
<h1 className="item-title m-0 text-primary text-bold">{item.title}</h1>
<div className="d-flex flex-column align-items-end justify-content-end">
<p className="m-0">{item.source} {item.type} | {item.mediatype} | {item.rating}</p>
<p className="m-0">Seasons: {item.seasons} ({item.episodes} episodes)</p>
</div>
</div>
<Row>
<Col xs={12} md={3} lg={2}>
<Card bg="dark">
<Card.Img variant="top" src={item.poster} />
</Card>
</Col>
<Col xs={12} md={9} lg={10}>
<Row className="media-info">
<Col
md={12}
lg={7}
className="media-container" >
<Card bg="dark" ref={divRef}>
<ReactVideo
src={item.trailer}
poster={item.trailerposter}
className="video-player"/>
</Card>
</Col>
<Col
md={12}
lg={5}
className="metadata-container" >
<PerfectScrollbar style={{ height: dimensions.height }}>
<Card bg="dark">
<Card.Body>
<div className="mb-3">
<h4 className="card-h4">Series Summary</h4>
<p className="card-p">{item.summary}</p>
</div>
<div>
<h4 className="card-h4">Series Metadata</h4>
<ul className="card-ul">
<li>
<label>Release Date: </label>
<span>{item.info[0].releasedate}</span>
</li>
<li>
<label>HBO Max First Availability Date: </label>
<span>{item.info[0].availabilitydate}</span>
</li>
<li>
<label>Runtime: </label>
<span>{item.info[0].runtime}</span>
</li>
<li>
<label>Genre(s): </label>
{ item.info[0].genres.map((genre, index) => {
return (
<span key={`genre_${index}`}>
{ (index ? ', ' : '') + genre }
</span>
)
})}
</li>
<li>
<label>Plot Tag(s): </label>
{ item.info[0].tags.map((tag, index) => {
return (
<span key={`tag_${index}`}>
{ (index ? ', ' : '') + tag }
</span>
)
})}
</li>
<li>
<label>Region Availability: </label>
{ item.info[0].regions.map((region, index) => {
return (
<span key={`region_${index}`}>
{ (index ? ', ' : '') + region }
</span>
)
})}
</li>
<li>
<label>Original Language: </label>
<span>{item.info[0].language}</span>
</li>
<li>
<label>Awards: </label>
{ item.info[0].awards.map((award, index) => {
return (
<span key={`award_${index}`}>
{ (index ? ', ' : '') + award }
</span>
)
})}
</li>
<li>
<label>Production Budget: </label>
<span>{item.info[0].prod_budget}</span>
</li>
<li>
<label>Marketing Budget: </label>
<span>{item.info[0].marketing_budget} </span>
</li>
</ul>
</div>
</Card.Body>
</Card>
</PerfectScrollbar>
</Col>
</Row>
</Col>
</Row>
</div>
)
})}
</div>
)
}
export default Home

You are creating a new ref every time setDimensions is called. Change createRef to useRef. I'd also remove the useRefDimensions function and just call useEffect directly.
const divRef = useRef(null)
const [dimensions, setDimensions] = useState({ width: 1, height: 2 })
useEffect(() => {
if (divRef.current) {
const { current } = divRef
const boundingRect = current.getBoundingClientRect()
const { width, height } = boundingRect
setDimensions({ width: Math.round(width), height: Math.round(height) })
}
}, [divRef])

Related

How to Display "No Items Were Found" React.js

I have a React.js App where I search pets as below
When the page first loads, I display some text and image. Then user makes a search and gets the results according to the search. But if there is no matching results, I want to display a message as "No Items were found"
How can I do this ?
My if condition is not working in the submit function.
Below is my code
Search.js
import React, { useState } from "react";
import SearchResults from "./SearchResults";
import { Container, Row, Col } from "react-bootstrap";
import { FaSearch } from "react-icons/fa";
import "./Search.css";
import { useFormik } from 'formik';
const Search = ({ searchGuests }) => {
const [searchResults, setSearchresults] = React.useState([])
const formik = useFormik({
initialValues: {
location: "",
age: "",
breed: "",
gender: "",
},
onSubmit: values => {
const searchItem = searchGuests.guestsResults.filter(item => {
return (
item.location
.toLowerCase()
.includes(
values.location.toLowerCase()
) &&
item.age
.toLowerCase()
.includes(
values.age.toLowerCase()
) &&
item.breed
.toLowerCase()
.includes(
values.breed.toLowerCase()
) &&
item.gender
.toLowerCase()
.includes(
values.gender.toLowerCase()
)
)
})
if(searchItem) {
return setSearchresults(searchItem)
} else {
return searchResults("No Items Were Found")
}
// return JSON.stringify(values, null, 2);
},
});
const LoadingPage = () => {
return (
<Col sm={12} className="loadPage">
<h4>Start Searching Now and Discover New Friends</h4>
<h6>They are waiting for their new home</h6>
<img src="https://timesofindia.indiatimes.com/photo/67586673.cms" className="w-100 searchCatLogo" />
</Col>
);
};
return (
<Container>
<Row className="searchContainer py-5">
<h1>{searchGuests.searchGuestsTitle.title}</h1>
<Col sm={12}>
<form onSubmit={formik.handleSubmit} className="searchForm">
<label htmlFor="location"></label>
<select id="location" name="location" value={formik.values.location} {...formik.getFieldProps('location')}>
<option value="Select Location">Select Location</option>
{searchGuests.locationCities.map(city => <option>{city}</option>)}
</select>
<label htmlFor="age"></label>
<input
id="age"
type="text"
placeholder="age"
{...formik.getFieldProps('age')}
/>
<label htmlFor="breed"></label>
<select id="breed" name="breed" value={formik.values.breed} {...formik.getFieldProps('breed')}>
<option value="Select Breed">Select Breed</option>
{searchGuests.searchBreed.map(breed => <option>{breed}</option>)}
</select>
<label htmlFor="gender"></label>
<select name="gender" value={formik.values.gender} {...formik.getFieldProps('gender')}>
<option value="Select Gender">Select Gender</option>
<option value="Female">Female</option>
<option value="Male">Male</option>
</select>
<button type="submit">
<FaSearch size={27} className="searchIcon" />
</button>
</form>
</Col>
<Col sm={12}>
<Row className="mt-5 items">
{searchResults.length > 0 ? searchResults.map(result => {
return (
<SearchResults
key={result.id}
img={result.img}
location={result.location}
age={result.age}
breed={result.breed}
gender={result.gender}
name={result.name}
/>
)
}) : <LoadingPage />}
</Row>
</Col>
</Row>
</Container>
);
};
export default Search;
SearchResults.js
import React, { useState } from "react";
import { Col, Button, Card } from "react-bootstrap";
import "./Search.css";
import { BsGeoAlt } from "react-icons/bs";
import { FaPaw } from "react-icons/fa";
import AdoptionFormModal from "./AdoptionFormModal"
const SearchResults = ({ img, id, location, age, breed, gender, name }) => {
const [modalShow, setModalShow] = useState(false);
return (
<>
<Col lg={4} className="mb-5">
<Card style={{ width: '18rem' }}>
<Card.Img variant="top" src={img} />
<Card.Body id="modalBody">
<Card.Text id="cardText">
<div className="firstDiv">
<p>{breed}</p>
<p>{name}</p>
<Button variant="primary" className="adoptButton shadow-none" onClick={() => setModalShow(true)}>
<FaPaw className="pawIcon"/>
Adopt
</Button>
</div>
<div className="secondDiv">
<p>{gender}, {age}</p>
<p className="mt-4"><BsGeoAlt size={25}/> {location}</p>
</div>
</Card.Text>
</Card.Body>
</Card>
</Col>
<AdoptionFormModal
show={modalShow}
onHide={() => setModalShow(false)}
/>
</>
);
};
export default SearchResults;
data.js
export let searchGuests = {
searchGuestsTitle: {
title: "Adopt Your New Pet",
},
guestsResults: [
{
id: 1,
img:
"https://imagesvc.meredithcorp.io/v3/mm/image?url=https%3A%2F%2Fstatic.onecms.io%2Fwp-content%2Fuploads%2Fsites%2F37%2F2020%2F09%2F22%2F50-cute-dog-names.jpg",
location: "Istanbul",
age: "2",
breed: "Poodle",
gender: "Female",
name: "Sushi",
},
{
id: 2,
img:
"https://imagesvc.meredithcorp.io/v3/mm/image?url=https%3A%2F%2Fstatic.onecms.io%2Fwp-content%2Fuploads%2Fsites%2F37%2F2020%2F09%2F22%2F50-cute-dog-names.jpg",
location: "Istanbul",
age: "1",
breed: "Terrier",
gender: "Male",
name: "Sushi",
},
{
id: 3,
img:
"https://imagesvc.meredithcorp.io/v3/mm/image?url=https%3A%2F%2Fstatic.onecms.io%2Fwp-content%2Fuploads%2Fsites%2F37%2F2020%2F09%2F22%2F50-cute-dog-names.jpg",
location: "Istanbul",
age: "2",
breed: "Poodle",
gender: "Female",
name: "Sushi",
},
],
locationCities : [
"Istanbul",
"Ankara",
"Izmir"
],
searchBreed : [
"Poodle",
"Terrier",
"Rottweiler",
"Golden Retriever",
"Cat",
"Tobby cat",
"Scottish"
],
searchAge : [
"<3",
">3",
"All"
]
};
You can render it based on the type of searchResults is a string or array
{/* Show result */}
{searchResults.length > 0 &&
Array.isArray(searchResults) &&
searchResults.map((result) => {
return (
<SearchResults
key={result.id}
img={result.img}
location={result.location}
age={result.age}
breed={result.breed}
gender={result.gender}
name={result.name}
/>
);
})}
{/* Show notfound */}
{searchResults.length > 0 && typeof searchResults === "string" && (
<div>{searchResults}</div>
)}
{/* Show loading */}
{searchResults.length === 0 && <LoadingPage />}
if(searchItem.length > 0) {
return setSearchresults(searchItem)
} else {
return searchResults("No Items Were Found")
}
First: You are directly mutated the state, so your component won't re-render:
return searchResults("No Items Were Found")
And also it's inefficient to save error state the same state you store your response, so create another state for that:
const [error, setError] = React.useState('')
And then set your error like this:
setError("No Items Were Found")
return;
Lastly, check for the existent of error and render it:
{error && <p>{error}</p>

When clicking on a row in the table, I need to open a modal, but how?

I use React with DataTable Component.
This is the library: https://www.npmjs.com/package/react-data-table-component#conditional-style-object
Here is my data table component:
import React, { useState, useEffect } from "react";
import EquipmentService from './EquipmentService';
import DataTable, { createTheme } from 'react-data-table-component';
import DataTableExtensions from "react-data-table-component-extensions";
import "react-data-table-component-extensions/dist/index.css";
import Downshift from 'downshift';
import { useCombobox } from 'downshift'
//Pacotes de import para os botoes de filtrar tabelas
import { purple } from '#material-ui/core/colors';
import FormGroup from '#material-ui/core/FormGroup';
import FormControlLabel from '#material-ui/core/FormControlLabel';
import Switch from '#material-ui/core/Switch';
import { withStyles } from '#material-ui/core/styles';
// reactstrap
import {
Card,
CardHeader,
Container,
Row,
} from "reactstrap";
import Header from "components/Headers/Headerfor.js";
import EditEquipment from "./EditEquipment";
import DeleteEquipment from "./DeleteEquipment";
import CreateEquipment from "./CreateEquipment";
const items = ['apple', 'pear', 'orange', 'grape', 'banana']
function ListEquipments() {
const [equipment, setEquipment] = useState([]);
useEffect(() => {
EquipmentService.getEquipment().then(res => setEquipment(res.data));
}, []);
//Estilo dos botoes de filtrar colunas
const PurpleSwitch = withStyles({
switchBase: {
color: purple[300],
'&$checked': {
color: purple[500],
},
'&$checked + $track': {
backgroundColor: purple[500],
},
},
checked: {},
track: {},
})(Switch);
const [state, setState] = React.useState({
ID: false,
Denominação: false,
Fabricante: false,
});
const handleChange = (event) => {
setState({ ...state, [event.target.name]: event.target.checked });
};
// FIM dos botoes
// tema da tabela, preciso colocar em outline para aplicar a todas.
const customStyles = {
rows: {
style: {
backgroundColor: '',
}
},
headCells: { //titulo da tabela.
style: {
paddingLeft: '8px', // override the cell padding for head cells
paddingRight: '8px',
backgroundColor: '#f6f9fc',
border: '1px solid #e5e5e5',
},
},
cells: {
style: {
paddingLeft: '8px', // override the cell padding for data cells
paddingRight: '8px',
borderLeft: '1px solid #e5e5e5',
},
},
};
const columns = [
/* {
name: 'ID',
selector: 'ID',
sortable: true,
omit: state.ID,
}, */
{
name: 'Código',
selector: 'codigo',
sortable: true,
grow: -1, //aumentar e diminuir o tamanho da coluna.
center: true, // centralizar a informação.
},
{
name: "Área",
selector: "area",
sortable: true,
//omit: state.Denominação,
},
{
name: "Setor Instalado",
selector: "setor",
sortable: true,
//omit: state.Denominação,
},
{
name: "Sistema",
selector: "sistema",
sortable: true,
//omit: state.Fabricante,
},
{
name: "Equipamento",
selector: "denominacao",
sortable: true,
},
{
name: "Fabricante",
selector: "fabricante",
sortable: true,
},
{
name: "Modelo de Equipamento",
selector: "modelo",
sortable: true,
},
];
const TranslateOptions = {
rowsPerPageText: 'Equipamentos por Página:',
rangeSeparatorText: 'de',
selectAllRowsItem: true,
selectAllRowsText: 'Todos',
}
const [inputItems, setInputItems] = useState(items)
const {
isOpen,
getToggleButtonProps,
getLabelProps,
getMenuProps,
getInputProps,
getComboboxProps,
highlightedIndex,
getItemProps,
} = useCombobox({
items: inputItems,
onInputValueChange: ({ inputValue }) => {
setInputItems(
items.filter(item =>
item.toLowerCase().startsWith(inputValue.toLowerCase()),
),
)
},
})
return (
<>
<Header />
{/* Page content */}
<Container className="mt--7" fluid>
{/* Table */}
<Row>
<div className="col">
<Card className="shadow">
<CardHeader className="border-0">
<h3 className="mb-0">Gestão de Equipamentos</h3>
{/* <FormGroup>
<FormControlLabel
control={<PurpleSwitch checked={state.ID} onChange={handleChange} name="ID" />}
label="ID"
/>
<FormControlLabel
control={<PurpleSwitch checked={state.Denominação} onChange={handleChange} name="Denominação" />}
label="Denominação"
/>
<FormControlLabel
control={<PurpleSwitch checked={state.Fabricante} onChange={handleChange} name="Fabricante" />}
label="Fabricante"
/>
</FormGroup> */}
<CreateEquipment />
</CardHeader>
<DataTableExtensions
columns={columns}
filterPlaceholder="Buscar"
filterHidden={false}
data={equipment.map((row) => ({
codigo: row.descricao,
area: row.area,
setor: row.setor_instalado,
sistema: row.sistema,
denominacao: row.denominacao,
fabricante: row.fabricante,
modelo: row.modelo,
//Status: row.status,
//Editar: <EditEquipment actual_id={row._id} />,
//Apagar: <DeleteEquipment actual_id={row._id} />,
}))}
>
<DataTable
noHeader
defaultSortField="id"
defaultSortAsc={false}
pagination
striped // sombra sim, sombra não.
responsive
highlightOnHover
onRowClicked={(row) => console.log(row.ID)}
//onRowClicked={EditEquipment}
customStyles={customStyles}
paginationComponentOptions={TranslateOptions}
/>
</DataTableExtensions>
</Card>
</div>
</Row>
</Container>
</>
);
};
in component: Data Table, i have: onRowClicked={(row) => console.log(row.ID)}
I need to click on a line and the modal appears.
This is the modal component, in a button.
function EditEquipment(props) {
const {
buttonLabel,
className,
actual_id,
} = props;
const [modal, setModal] = useState(false);
const toggle = () => setModal(!modal);
const [area, setArea] = useState('');
const [denominacao, setDenominacao] = useState('');
const [descricao, setDescricao] = useState('');
const [fabricante, setFabricante] = useState('');
const [modelo, setModelo] = useState('');
const [sistema, setSistema] = useState('');
const [setor_instalado, setSetor_instalado] = useState('');
const handleSubmit = (evt) => {
evt.preventDefault();
//alert(`Submitting Name ${firstName} ${lastName}`);
//resetFirstName();
//resetLastName();
}
const update = (evt) => {
let equipments = {area: area, denominacao: denominacao, descricao: descricao, fabricante: fabricante, modelo: modelo, setor_instalado: setor_instalado};
console.log(equipments)
EquipmentService.updateEquipment(equipments, actual_id).then(res =>{ //equipment._id no lugar do ID
console.log(res);
setModal(!modal);
//console.log("Etapa 4");
window.location.reload();
console.log("Etapa 5");
}).catch(error => console.log(error.response));
}
useEffect(() => {
async function getEquipment(){
EquipmentService.getEquipmentById(actual_id).then( (res) =>{
let equipments = res.data;
setArea(equipments.area); //equipments.tag
setDenominacao(equipments.denominacao);
setDescricao(equipments.descricao);
setFabricante(equipments.fabricante);
setSistema(equipments.sistema);
setModelo(equipments.modelo);
setSetor_instalado(equipments.setor_instalado);
});
}
getEquipment();
}, []);
return (
<div>
<Button color="success" size="sm" className="" onClick={toggle}> Editar </Button>
<Modal isOpen={modal} toggle={toggle} size="lg" className="mr-0 mt--1 mb--1">
<ModalHeader toggle={toggle}>Editar Equipamento </ModalHeader>
<ModalBody>
<form onSubmit={handleSubmit}>
<div className = "form-group">
<label> Área: </label>
<input name="tag" className="form-control"
value={area}
onChange={e => setArea(e.target.value)}
/>
</div>
<div className = "form-group">
<label> Setor Instalado: </label>
<input name="setor_instalado" className="form-control"
value={setor_instalado}
onChange={e => setSetor_instalado(e.target.value)}
/>
</div>
<div className = "form-group">
<label> Sistema: </label>
<input name="sistema" className="form-control"
value={sistema}
onChange={e => setSistema(e.target.value)}
/>
</div>
<div className = "form-group">
<label> Equipamento: </label>
<input name="equipamento" className="form-control"
value={denominacao}
onChange={e => setDenominacao(e.target.value)}
/>
</div>
<div className = "form-group">
<label> Código do Equipamento: </label>
<input name="Código" className="form-control"
value={descricao}
onChange={e => setDescricao(e.target.value)}
/>
</div>
<div className = "form-group">
<label> Fabricante: </label>
<input name="setor_instalado" className="form-control"
value={fabricante}
onChange={e => setFabricante(e.target.value)}
/>
</div>
<div className = "form-group">
<label> Modelo: </label>
<input name="modelo" className="form-control"
value={modelo}
onChange={e => setModelo(e.target.value)}
/>
</div>
</form>
<div className = "form-group">
<Card className=" shadow-lg bg-default">
<CardHeader className="">
<Row>
<Col>
<h4> QR Code: </h4>
</Col>
<Col>
</Col>
<Col >
<Button color="default" size="sm" className="ml-9" type="button">
Imprimir
</Button>
</Col>
</Row>
</CardHeader>
<CardBody>
<Row>
<Col className="">
<QRCode position="" value="http://www.darwinx.com.br" />
</Col>
<Col className="ml--6 pr-9 text-white">
<label> Número:</label> <br></br>
<label> Equipamento:</label> <br></br>
<label> Descrição:</label>
</Col>
<Col></Col>
</Row>
</CardBody>
</Card>
</div>
<Button color="success" onClick={update} >Atualizar</Button>{' '}
<Button color="secondary" onClick={toggle}>Cancelar</Button>
</ModalBody>
<ModalFooter>
</ModalFooter>
</Modal>
</div>
);
}
please someone help me?
ps: sorry for my english, i not speak english, and i use the google tradutor for need help.
Add the modal inside the return of the data table component. Add these on your data table component as well:
const [modal, setModal] = useState(false);
const toggle = () => setModal(!modal);
Then you can do:
onRowClicked={(row) => setModal(true)}
You should also probably store the currently selected equipment using a state hook. And make the modal read from it.

How to create a resposnive search filter with React

I have a DogCardsDisplayed React Component which maps through an Array of objects that displays the data in cards to the page. My SearchBar Component searches through the the array and filters the list in the console based on user search. How do I apply that to the actual cards so that the cards themselves are filtered on the page.
const dogData = [
{
id: 1,
name: "Hollie",
breed: "Doberman",
age: 3,
weight: 20,
height: 150,
img: "https://placedog.net/500/200?id=61",
},
{
id: 2,
name: "Charles",
breed: "Beagle",
age: 4,
weight: 20,
height: 150,
img: "https://placedog.net/500/200?id=100",
}
import DogCardsDisplayed from "./DogCardsDisplayed";
import dogData from "./dogData";
import { Nav, Navbar, Button } from "react-bootstrap";
function SearchBar() {
const onSub = (e) => {
let substring = e.target.value;
let filteredData = dogData.filter(
(item) =>
item.name.toLowerCase().includes(substring.toLowerCase()) ||
item.breed.toLowerCase().includes(substring.toLowerCase())
);
console.log(filteredData);
};
return (
<Navbar className="d-block">
<form>
<input
onChange={onSub}
className="search-input"
placeholder="Search"
name="search"
></input>
<Button
variant="outline-light"
className="search-button"
type="submit"
>
Search
</Button>
</form>
</Navbar>
);
}
function DogCardsDisplayed() {
return dogData.map((item) => (
<Card key={item.id} className="card">
<div>
<Card.Img variant="top" src={item.img} />
<Card.Body>
<Card.Title>{item.name}</Card.Title>
<div className="d-flex">
<div><b>Breed:</b> {item.breed}</div>
<div><b>Age:</b> {item.age}</div>
<div><b>Weight:</b> {item.weight}lb</div>
<div><b>Height:</b> {item.height}in</div>
</div>
</Card.Body>
</div>
</Card>
));
}
function SearchPage() {
return (
<>
<SearchBar />
<div className="d-flex flex-wrap sp-body">
<DogCardsDisplayed />
</div>
</>
);
}
export default SearchPage;
You can achieve this by storing the filter in the state (useState)[1] and then using useMemo [2] to return the filtered data based on changes to the state filter and the dogData.
function SearchBar({ onSearch }) {
const onSub = (e) => {
onSearch(e.target.value.toLowerCase());
};
return (
<Navbar className="d-block">
<form>
<input
onChange={onSub}
className="search-input"
placeholder="Search"
name="search"
/>
</form>
</Navbar>
);
}
function DogCardsDisplayed({ dogData }) {
return dogData.map((item) => (
<Card key={item.id} className="card">
<div>
<Card.Img variant="top" src={item.img} />
<Card.Body>
<Card.Title>{item.name}</Card.Title>
<div className="d-flex">
<div><b>Breed:</b> {item.breed}</div>
<div><b>Age:</b> {item.age}</div>
<div><b>Weight:</b> {item.weight}lb</div>
<div><b>Height:</b> {item.height}in</div>
</div>
</Card.Body>
</div>
</Card>
));
}
function SearchPage() {
const [filter, setFilter] = React.useState("");
const filteredData = React.useMemo(() => {
if (filter == "") return dogData;
return dogData.filter(
(item) =>
item.name.toLowerCase().includes(filter) ||
item.breed.toLowerCase().includes(filter)
);
}, [dogData, filter]);
return (
<>
<SearchBar onSearch={(searchTerm) => setFilter(searchTerm)}/>
<div className="d-flex flex-wrap sp-body">
<DogCardsDisplayed dogData={filteredData} />
</div>
</>
);
}
References:
[1] useState hook - https://reactjs.org/docs/hooks-state.html
[2] useMemo hook - https://reactjs.org/docs/hooks-reference.html#usememo

React read string as component

I'm using bootstrap framework to render a simple 3 column grid layout out of some content on a page. Where it gets complicated, I'm trying to conditionally render bootstrap <Row> or </Row> depending on where we are in the loop - for instance ,render a <Row> if index % 3 == 0 and render a </Row> if (index + 1) % 3 == 0. Because there are problems with conditional rendering in map, I've come up with the following approach which would work if there was a way to read a string as a React component. Does anyone know how to do this?
const websiteData = [
{
name: "abcdefg",
url: "https://www.abcedfg.com/",
categories: ["react"],
thumbnail: require("./assets/images/image.png"),
},
{
name: "rrrrrr",
url: "https://www.rrrrrr.com/",
categories: ["wordpress"],
thumbnail: require("./assets/images/rrrrrr.png"),
},
{
name: "jjjjjjjj",
url: "https://www.jjjjjjjj.com/",
categories: ["wordpress"],
thumbnail: require("./assets/images/jjjjjjjj.png"),
},
{
name: "ffffff",
url: "https://www.ffffff.com/",
categories: ["wordpress"],
thumbnail: require("./assets/images/ffffff.png"),
},
{
name: "dddddd",
url: "https://www.dddddd.com/",
categories: ["wordpress"],
thumbnail: require("./assets/images/dddddd.png"),
},
{
name: "adadads",
url: "https://www.adadads.com/",
categories: ["wordpress"],
thumbnail: require("./assets/images/adadads.png"),
},
];
const RowOpen = (props) => {
const { index } = props;
if (index % 3 == 0) {
return "<Row>";
} else {
return <span />;
}
};
const RowClose = (props) => {
const { index } = props;
if (index + (1 % 3) == 0) {
return "</Row>";
} else {
return <span />;
}
};
const Websites = () => {
return websiteData.map((site, index) => {
return (
<React.Fragment>
<RowOpen index={index} />
<Col md="4">
<a>
<img style={{ maxWidth: "100%" }} src={site.thumbnail} />
</a>
</Col>
<RowClose index={index} />
</React.Fragment>
);
});
};
Ultimately I want to loop through websiteData and wind up with something like this:
<Row>
<Col md=4">
...
</Col>
<Col md=4">
...
</Col>
<Col md=4">
...
</Col>
</Row>
<Row>
<Col md=4">
...
</Col>
<Col md=4">
...
</Col>
<Col md=4">
...
</Col>
</Row>
You have <RowOpen index={index} />...<RowClose index={index} />, and then these are defined as returning "<Row>" and "</Row>". To be clear, these are strings, and will display as plain text, and not as components. You cannot define components like this in ReactJS. You must define them like so...
var component = <Row />;
Just try it with your approach in an a sandbox online:
Expected corresponding JSX closing tag for
Do this instead: Make a display function for each row...
const Websites = () => {
return (
websiteData.map((site, index) => {
return (
<React.Fragment>
{RowDisplay(site, index)}
</React.Fragment>
)
})
)
}
And then have your RowDisplay() function display either either <Row>'s or <span>'s...
const RowDisplay = (site, index) => {
if (index + 1 % 3 == 0) {
return RowDisplayRow(site, index);
} else {
return RowDisplaySpan(site, index);
}
}
Then you just need the final Row/Span display functions...
const RowDisplayRow = (site, index) => {
return (
<Row index={index}>
<Col md="4">
<a>
<img style={{ maxWidth: '100%' }} src={site.thumbnail} />
</a>
</Col>
</Row index={index}>
);
}
const RowDisplaySpan = (site, index) => {
return (
<Row index={index}>
<Col md="4">
<a>
<img style={{ maxWidth: '100%' }} src={site.thumbnail} />
</a>
</Col>
</Row index={index}>
);
}
I have done this all in sudo-code, but, I think you'll probably get the idea I'm trying to demonstrate. Explanation is sometimes much more important than just code!
You just simply put the conditions in the map like
return (
websiteData.map((site, index) => {
return (
<React.Fragment>
{index % 3 == 0 ? <Row /> : <span />}
<Col md="4">
<a>
<img style={{ maxWidth: '100%' }} src={site.thumbnail} />
</a>
</Col>
{index + 1 % 3 == 0 ? </Row> : <span /> }
</React.Fragment>
)
})
)
Try this You will get your desired output ! It will help
Simple and best approach ! :)

React table row selection

I've followed below link and tried to construct a table grid which includes row selection.
https://codesandbox.io/embed/github/tannerlinsley/react-table/tree/master/examples/row-selection
But for some reason it's not working. It's allowing me to select any two rows. When I select 3rd row the previous selection isn't remembered.
Here is the code snippet.
import React, { useState, useEffect, useRef } from "react";
import { useQuery } from "#apollo/react-hooks";
import { ContentWrapper } from "#nextaction/components";
import { useTable, useRowSelect, usePagination } from "react-table";
import $ from "jquery";
import { Row, Col, Button, Badge } from "reactstrap";
import FETCH_XXXXX_QUERY from "../queries/FetchXXXXXQuery";
import { Link } from "react-router-dom";
const IndeterminateCheckbox = React.forwardRef(
({ indeterminate, ...rest }, ref) => {
const defaultRef = React.useRef();
const resolvedRef = ref || defaultRef;
React.useEffect(() => {
resolvedRef.current.indeterminate = indeterminate;
}, [resolvedRef, indeterminate]);
return (
<>
<input type="checkbox" ref={resolvedRef} {...rest} />
</>
);
}
);
function XxxxxxGrid({ columns, data, prospectData }) {
// Use the state and functions returned from useTable to build your UI
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
selectedFlatRows,
state: { selectedRowIds },
} = useTable(
{
columns,
data,
prospectData,
},
useRowSelect,
(hooks) => {
hooks.visibleColumns.push((columns) => [
// Let's make a column for selection
{
id: "selection",
// The header can use the table's getToggleAllRowsSelectedProps method
// to render a checkbox
Header: ({ getToggleAllRowsSelectedProps }) => (
<div>
<IndeterminateCheckbox {...getToggleAllRowsSelectedProps()} />
</div>
),
// The cell can use the individual row's getToggleRowSelectedProps method
// to the render a checkbox
Cell: ({ row }) => (
<div>
<IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />
</div>
),
},
...columns,
]);
}
);
// Render the UI for your table
return (
<table {...getTableProps()}>
<thead>
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
<th {...column.getHeaderProps()}>{column.render("Header")}</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.slice(0, 10).map((row, i) => {
prepareRow(row);
return (
<tr {...row.getRowProps()}>
{row.cells.map((cell) => {
return <td {...cell.getCellProps()}>{cell.render("Cell")}</td>;
})}
</tr>
);
})}
</tbody>
</table>
);
}
const Xxxxxxxx = ({ actions, history }) => {
const { data, loading, error } = useQuery(QUERY);
const gridData = React.useMemo(() => (data ? data.xxxxx.data : []), [data]);
const [activeTab, setActiveTab] = useState("ALL");
const columns = React.useMemo(
() => [
{
Header: "Name",
accessor: "contactName",
Cell: function (props) {
return (
<span>
<b>
<Link to={"/xxxxx/" + props.row.original.id}>
{props.value}
</Link>
</b>
<br></br>
<span>{props.row.original.title}</span>
<br></br>
<Link to={"/xxxxx/" + props.row.original.id}>
{props.row.original.accountName}
</Link>
</span>
);
},
},
{
Header: "Cadence",
accessor: "accountName",
Cell: function (props) {
return (
<span>
<b>
<Link to={"/xxxxxxxx/" + cadence.id}>{props.value}</Link>
</b>
</span>
);
},
},
{
Header: "Tags",
accessor: "tag",
Cell: function (props) {
return (
<Badge color="secondary" pill>
{props.value}
</Badge>
);
},
},
{
Header: "C Outcome",
accessor: "phone",
},
{
Header: "E Outcome",
accessor: "title",
},
{
Header: "Last Contact",
accessor: "createdDate",
},
],
[]
);
const XxxxxxGrid = React.useMemo(
() => (
<XxxxxxGrid
columns={columns}
data={gridData}
prospectData={prospectData}
/>
),
[gridData]
);
return (
<ContentWrapper>
<div className="content-heading pb-0">
<div>
<em className="fa fa-user fa-lg"></em> Prospects
</div>
<div className="ml-auto">
<ul className="nav nav-tabs">
<li className="nav-item">
<a
href="#"
className={
"nav-link text-center" +
(activeTab === "xxxx" ? " active" : "")
}
>
<h4 className="text-primary">1522</h4>
<h6>xxx</h6>
</a>
</li>
<li className="nav-item">
<a
href="#"
className={
"nav-link text-center" +
(activeTab === "xxxx" ? " active" : "")
}
>
<h4 className="text-primary">1522</h4>
<h6>xxxx</h6>
</a>
</li>
<li className="nav-item">
<a
href="#"
className={
"nav-link text-center" +
(activeTab === "xxxx" ? " active" : "")
}
>
<h4 className="text-primary">1522</h4>
<h6>xxx</h6>
</a>
</li>
<li className="nav-item">
<a
href="#"
className={
"nav-link text-center" +
(activeTab === "xxxxxx" ? " active" : "")
}
>
<h4 className="text-primary">1522</h4>
<h6>xxxxx</h6>
</a>
</li>
</ul>
</div>
</div>
<Row className="mb-3">
<Col sm={4}>
<div className="input-group pl-0">
<input
type="text"
className="form-control"
placeholder="Search"
aria-label="Search"
aria-describedby="button-addon2"
/>
<div className="input-group-append">
<Button
color="outline-secondary"
type="button"
id="button-addon2"
>
<i className="fa fa-search"></i>
</Button>
</div>
</div>
</Col>
</Row>
<Row className="border-bottom border-top">
<div className="ml-auto">
<Button color="outline">
<em className="fas fa-file-csv text-primary"></em> Import CSV
</Button>
|
<Button color="outline">
<em className="fa fa-plus text-primary"></em> Add
</Button>
</div>
</Row>
<Row>{!loading && XxxxxxGrid}</Row>
</ContentWrapper>
);
};
export default Xxxxxxxx;
Something similar happened to me,
in my case the bug was generated due to a conflict with the StrictMode generated by default in index.js.
Try to Delete it
ReactDOM.render (
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById ('root')
the Index should look like this:
ReactDOM.render (
<App />,
document.getElementById ('root')

Resources