view more and view less with useState hook in react js - javascript

I am fresh to react and useState hooks (still learning). I want to create show more/less button with use of an Array of images and React hooks. I import the images into div, I wanted to set button on the last and the show the images on the click of button.
I am getting the error:
text.slice is not function
The thing is, the code is written with use of function component.
here's my code:
import React from 'react';
import { useState } from 'react';
import '../assets/css/Product.css';
const ReadMore = ({ children }) => {
const text = children;
const [isReadMore, setIsReadMore] = useState(true);
const toggleReadMore = () => {
setIsReadMore(!isReadMore);
};
return (
<p className='text'>
{isReadMore ? text.slice(0, 150) : text}
<span onClick={toggleReadMore} className='read-or-hide'>
{isReadMore ? '...read more' : ' show less'}
</span>
</p>
);
};
export const Product = () => {
const card_image = [
{
image: 'CLT_Food_BeverageBar.jpg',
title: 'Charlotte',
subtitle: '(CLT)',
button: 'Explore Lounge',
},
{
image: 'Centurion_Cropped_0001_Bar.jpg',
title: 'Dallas',
subtitle: '(DFW)',
button: 'Explore Lounge',
},
{
image: 'DEN_GameRoom-LR_1200x540.jpg',
title: 'Denver',
subtitle: '(DEN)',
button: 'Explore Lounge',
},
{
image: 'IAH_Bar&Buffet_1200x600.jpg',
title: 'Houston',
subtitle: '(IAH)',
button: 'Explore Lounge',
},
{
image: 'amxtcl_Lounge_01_cmyk_1200x600.jpg',
title: 'Las Vegas',
subtitle: '(LAS)',
button: 'Explore Lounge',
},
{
image: 'LAX_hero.jpg',
title: 'Los Angeles',
subtitle: '(LAX)',
button: 'Explore Lounge',
},
{
image: 'LoungeAreaTalent1200x600.jpg',
title: 'Miami',
subtitle: '(MIA)',
button: 'Explore Lounge',
},
{
image: 'JFK_Carousel_3.jpg',
title: 'New York',
subtitle: '(JFX)',
button: 'Explore Lounge',
},
];
return (
<div>
<div className='container'>
<ReadMore>
<div className='row introduction'>
{card_image.map((card) => (
<div className='col-lg-3 pt-5'>
<div
className='location_card'
style={{
backgroundImage: `url(${process.env.REACT_APP_ASSET_URL}/card_image/${card.image})`,
objectFit: 'cover',
}}>
<div className='location-overly'>
<h3 className='h2'>
{card.title}
<br />
{card.subtitle}
</h3>
<button
type='button'
class='btn_product '>
{card.button}
</button>
</div>
</div>
</div>
))}
</div>
</ReadMore>
</div>
</div>
);
};

I think you should use conditional rendering, this means that when a state changes, your UI also changes. Sorry, I don't think I explained it that well, so here's some example code.
import * from x "xyz xyz";
const App = () => {
const [showMore, setShowMore] = useState(false);
if(showMore){
return(
<MoreStuff/>
);
}
return(
<DefaultStuff/>
);
}
Resources:
https://www.digitalocean.com/community/tutorials/7-ways-to-implement-conditional-rendering-in-react-applications
https://www.w3schools.com/react/react_conditional_rendering.asp
https://zh-hans.reactjs.org/docs/conditional-rendering.html

import { useState } from 'react';
const allCars = [
{ name: 'Audi', country: 'Germany' },
{ name: 'BMW', country: 'Germany' },
{ name: 'Chevrolet', country: 'USA' },
{ name: 'Citroen', country: 'France' },
{ name: 'Hyundai', country: 'South Korea' },
{ name: 'Mercedes-Benz', country: 'Germany' },
{ name: 'Renault', country: 'France' },
{ name: 'Seat', country: 'Spain' },
];
function CarFilter() {
const [showMoreData, setShowMoreData] = useState(3);
const showMore = () => {
if (showMoreData === 3) {
setShowMoreData(allCars.length);
} else if (showMoreData > 3) {
setShowMoreData(3);
}
};
return (
<div className="container">
<ul>
{allCars.slice(0, showMoreData).map((car, i) => (
<li key={i}>
{car.name} - {car.country}
</li>
))}
</ul>
{allCars.length > 3 && (
<button type="button" onClick={showMore}>
{showMoreData > 3 ? 'Show More ' : 'Show less'}
</button>
)}
</div>
);
}
export default CarFilter;

Related

Avoiding infinite useEffect loop while reflecting state change in React

I am trying to create an app where the user can click on a category and it will set the displayWork array to the value of the category and thus display the items in that array.
I have all of the work stored in an array
const [workList, setWorkList] = useState([
{ title: "aaa", image: "./images/1.jfif", type: "landscaping" },
{ title: "plumbing", image: "./images/2.jfif", type: "plumbing" },
{ title: "Other 3", image: "./images/3.png", type: "other3" },
{ title: "Other 4", image: "./images/3.png", type: "other4" },
]);
and then I have my displayWork mapped to the page, I plan on changing it through using array.filter()
let filteredWork = workList.filter(function (work) {
return work.type === catagory;
});
then I have a row of categories like this to set the parameters of the filter
<div className="col-sm-2 workCatagory" onClick={() => setCatagory("landscaping")}>
Land Scaping
</div>
<div className="col-sm-2 workCatagory" onClick={() => setCatagory("plumbing")}>
plumbing
</div>
<div className="col-sm-2 workCatagory" onClick={() => setCatagory("other3")}>
test3
</div>
<div className="col-sm-2 workCatagory" onClick={() => setCatagory("other4")}>
Test4
</div>
And then I am setting the displayWork to the value of the filtered array like so:
<div className="row workBars" onClick={() => handleClick()}>
const handleClick = () => {
setDisplayWork(filteredWork)
}
The problem with this approach is the change is not reflected immediately upon clicking the button. Using this solution here I was able to fix this
useEffect(() => {
handleClick()
}, [handleClick])
But I ran into an error message and the page crashing on me very often, I cannot get the state to update immediately without also creating an infinite loop. A link to my full page is here github I appreciate any advice you may have
Because you use non-state variables to dependencies. So you need to wrap it into useMemo, useCallback to avoid infinitive loop
const filteredWork = useMemo(
() =>
workList.filter(function (work) {
return work.type === catagory;
}),
[workList, category],
);
const handleClick = useCallback(() => {
setDisplayWork(filteredWork);
}, [setDisplayWork, filteredWork]);
I just changed the architecture of the component.
Instead of calling two functions (setCategory and handleClick) for a single action is not a good idea.
So I added an useEffect hook, which gets triggered whenever the category is changed. In this way, the whole execution will be somehow synchronous.
import React, { useState, useEffect } from "react";
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
export const Work = () => {
gsap.registerPlugin(ScrollTrigger);
const [workList, setWorkList] = useState([
{ title: "aaa", image: "./images/1.jfif", type: "landscaping" },
{ title: "aaa", image: "./images/1.jfif", type: "landscaping" },
{ title: "aaa", image: "./images/1.jfif", type: "landscaping" },
{ title: "aaa", image: "./images/1.jfif", type: "landscaping" },
{ title: "aaa", image: "./images/1.jfif", type: "landscaping" },
{ title: "aaa", image: "./images/1.jfif", type: "landscaping" },
{ title: "plumbing", image: "./images/2.jfif", type: "plumbing" },
{ title: "plumbing", image: "./images/2.jfif", type: "plumbing" },
{ title: "plumbing", image: "./images/2.jfif", type: "plumbing" },
{ title: "plumbing", image: "./images/2.jfif", type: "plumbing" },
{ title: "plumbing", image: "./images/2.jfif", type: "plumbing" },
{ title: "plumbing", image: "./images/2.jfif", type: "plumbing" },
{ title: "plumbing", image: "./images/2.jfif", type: "plumbing" },
{ title: "plumbing", image: "./images/2.jfif", type: "plumbing" },
{ title: "Other 3", image: "./images/3.png", type: "other3" },
{ title: "Other 4", image: "./images/3.png", type: "other4" },
]);
const [displayWork, setDisplayWork] = useState([
{ title: "aaa", image: "./images/test.png", type: "landscaping" },
{ title: "aaa", image: "./images/test.png", type: "landscaping" },
{ title: "aaa", image: "./images/test.png", type: "landscaping" },
{ title: "aaa", image: "./images/test.png", type: "landscaping" },
{ title: "aaa", image: "./images/test.png", type: "landscaping" },
]);
const [catagory, setCatagory] = useState("Select a catagory");
//let newArray = workList.filter(workList.type.includes(catagory))
const Animation = () => {
console.log('woo')
}
useEffect(() => {
let filteredWork = workList.filter(function (work) {
return work.type === catagory;
});
setDisplayWork(filteredWork);
}, [catagory, workList]);
return (
<div className="workPage" id="workPage">
<h1 className="text-center">Here's what we can do</h1>
<div className="row workBars">
<div className="col-sm-2"></div>
<div className="col-sm-2 workCatagory" onClick={() => setCatagory("landscaping")}>
Land Scaping
</div>
<div className="col-sm-2 workCatagory" onClick={() => setCatagory("plumbing")}>
plumbing
</div>
<div className="col-sm-2 workCatagory" onClick={() => setCatagory("other3")}>
test3
</div>
<div className="col-sm-2 workCatagory" onClick={() => setCatagory("other4")}>
Test4
</div>
</div>
<h1> {catagory}</h1>
<div className="row">
<div className="col-sm-2"></div>
{displayWork.map((displayWork) => (
<div className="col-sm-4">
{" "}
<br />
<p>{displayWork.title}</p>
<img
src={displayWork.image}
alt={displayWork.name}
className="img-fluid workImage"
></img>
</div>
))}
<div className="col-sm-2"></div>
</div>
</div>
);
};

How do I get hover effect on rows with material-table react?

I have used material-table with react to render my data. I want to show the hover effect and cursor pointer effect when I hover on rows. But I couldn't find this even in the documentation.
IN SHORT- I want to highlight some colors on a row when the cursor hovers to that row.
Note: I also found a similar question and answer here, but they used another state just to hover which downgrades the performance if I increase the data like thousands rows. Hence it's bad practice so here I am asking for alternate solutions for the same.
Here is my codesandbox link
Below I also pasted my code.
App.js
import React, { useState } from 'react';
import './App.css';
import MaterialTable from 'material-table'
const empList = [
{ id: 1, name: "Neeraj", email: 'neeraj#gmail.com', phone: 9876543210, city: "Bangalore" },
{ id: 2, name: "Raj", email: 'raj#gmail.com', phone: 9812345678, city: "Chennai" },
{ id: 3, name: "David", email: 'david342#gmail.com', phone: 7896536289, city: "Jaipur" },
{ id: 4, name: "Vikas", email: 'vikas75#gmail.com', phone: 9087654321, city: "Hyderabad" },
]
function App() {
const [data, setData] = useState(empList)
const columns = [
{ title: "ID", field: "id", editable: false },
{ title: "Name", field: "name" },
{ title: "Email", field: "email" },
{ title: "Phone Number", field: 'phone', },
{ title: "City", field: "city", }
]
return (
<div className="App">
<h1 align="center">React-App</h1>
<h4 align='center'>Material Table with CRUD operation</h4>
<MaterialTable
title="Employee Data"
data={data}
columns={columns}
editable={{
onRowAdd: (newRow) => new Promise((resolve, reject) => {
const updatedRows = [...data, { id: Math.floor(Math.random() * 100), ...newRow }]
setTimeout(() => {
setData(updatedRows)
resolve()
}, 2000)
}),
onRowDelete: selectedRow => new Promise((resolve, reject) => {
const index = selectedRow.tableData.id;
const updatedRows = [...data]
updatedRows.splice(index, 1)
setTimeout(() => {
setData(updatedRows)
resolve()
}, 2000)
}),
onRowUpdate:(updatedRow,oldRow)=>new Promise((resolve,reject)=>{
const index=oldRow.tableData.id;
const updatedRows=[...data]
updatedRows[index]=updatedRow
setTimeout(() => {
setData(updatedRows)
resolve()
}, 2000)
})
}}
options={{
actionsColumnIndex: -1, addRowPosition: "first"
}}
/>
</div>
);
}
export default App;
add in your inside of your CSS
tbody tr:hover {
background-color: blue;
color: white;
cursor: pointer;
}

How can I change the color of material-table icons of onRowAdd, onRowUpdate, onRowDelete?

I tried to the material-table the library for basic crud operation. By using onRowAdd, onRowUpdate, onRowDelete, I get the icons for the same but I would like to know that how can I change the color of each of these three icons?
You can see my table has few icons and I am focusing on add, edit, delete icons I want to change color of these icons.
Here is the link to my codesandbox.
App.js file
import React, { useState } from 'react';
import './App.css';
import MaterialTable from 'material-table'
const empList = [
{ id: 1, name: "Neeraj", email: 'neeraj#gmail.com', phone: 9876543210, city: "Bangalore" },
{ id: 2, name: "Raj", email: 'raj#gmail.com', phone: 9812345678, city: "Chennai" },
{ id: 3, name: "David", email: 'david342#gmail.com', phone: 7896536289, city: "Jaipur" },
{ id: 4, name: "Vikas", email: 'vikas75#gmail.com', phone: 9087654321, city: "Hyderabad" },
]
function App() {
const [data, setData] = useState(empList)
const columns = [
{ title: "ID", field: "id", editable: false },
{ title: "Name", field: "name" },
{ title: "Email", field: "email" },
{ title: "Phone Number", field: 'phone', },
{ title: "City", field: "city", }
]
return (
<div className="App">
<h1 align="center">React-App</h1>
<h4 align='center'>Material Table with CRUD operation</h4>
<MaterialTable
title="Employee Data"
data={data}
columns={columns}
editable={{
onRowAdd: (newRow) => new Promise((resolve, reject) => {
const updatedRows = [...data, { id: Math.floor(Math.random() * 100), ...newRow }]
setTimeout(() => {
setData(updatedRows)
resolve()
}, 2000)
}),
onRowDelete: selectedRow => new Promise((resolve, reject) => {
const index = selectedRow.tableData.id;
const updatedRows = [...data]
updatedRows.splice(index, 1)
setTimeout(() => {
setData(updatedRows)
resolve()
}, 2000)
}),
onRowUpdate:(updatedRow,oldRow)=>new Promise((resolve,reject)=>{
const index=oldRow.tableData.id;
const updatedRows=[...data]
updatedRows[index]=updatedRow
setTimeout(() => {
setData(updatedRows)
resolve()
}, 2000)
})
}}
options={{
actionsColumnIndex: -1, addRowPosition: "first"
}}
/>
</div>
);
}
export default App;
You can override the icons and provide custom styles by setting the icons props. It accepts an object where the key is a type of operation (Add, Edit, Delete,...) and the value is an icon component. For reference, see the all-props section here.
<MaterialTable
{...props}
icons={{
Edit: () => <EditIcon style={{ color: "orange" }} />,
Delete: () => <DeleteIcon style={{ color: "red" }} />
}}
>
Live Demo
It's Simple. Inspect on the page and Select the Icon and Copy its style Name in Styles Tab.
Now, Go to App.css file and Create New Style with the icon style name shown on Inspect-styles area and there you can enter your desired color.
It will work.
In your App.css File,
Add below code
.MuiIconButton-colorInherit {
color: red;
}
change to any color

converting antd table data into hooks

React beginner here, i'm currently learning Reactjs, since everybody is saying React hooks is easier to start with, so i started with hooks, but everywhere is mainly react with classes, here in this example (antd table) it is coded using classes, if i have understood it currently, i should put 'searchText:'',searchedColumn:'',' into useState, and dont need useEffect ? since there is no componentdimount or udpate .. ? just for learning want to change this class code into Hooks, sory for mistakes because english is not my mother language:
import React from 'react';
import ReactDOM from 'react-dom';
import 'antd/dist/antd.css';
import './index.css';
import { Table, Input, Button, Space } from 'antd';
import Highlighter from 'react-highlight-words';
import { SearchOutlined } from '#ant-design/icons';
const data = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
},
{
key: '2',
name: 'Joe Black',
age: 42,
address: 'London No. 1 Lake Park',
},
{
key: '3',
name: 'Jim Green',
age: 32,
address: 'Sidney No. 1 Lake Park',
},
{
key: '4',
name: 'Jim Red',
age: 32,
address: 'London No. 2 Lake Park',
},
];
class App extends React.Component {
state = {
searchText: '',
searchedColumn: '',
};
getColumnSearchProps = dataIndex => ({
filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => (
<div style={{ padding: 8 }}>
<Input
ref={node => {
this.searchInput = node;
}}
placeholder={`Search ${dataIndex}`}
value={selectedKeys[0]}
onChange={e => setSelectedKeys(e.target.value ? [e.target.value] : [])}
onPressEnter={() => this.handleSearch(selectedKeys, confirm, dataIndex)}
style={{ width: 188, marginBottom: 8, display: 'block' }}
/>
<Space>
<Button
type="primary"
onClick={() => this.handleSearch(selectedKeys, confirm, dataIndex)}
icon={<SearchOutlined />}
size="small"
style={{ width: 90 }}
>
Search
</Button>
<Button onClick={() => this.handleReset(clearFilters)} size="small" style={{ width: 90 }}>
Reset
</Button>
<Button
type="link"
size="small"
onClick={() => {
confirm({ closeDropdown: false });
this.setState({
searchText: selectedKeys[0],
searchedColumn: dataIndex,
});
}}
>
Filter
</Button>
</Space>
</div>
),
filterIcon: filtered => <SearchOutlined style={{ color: filtered ? '#1890ff' : undefined }} />,
onFilter: (value, record) =>
record[dataIndex]
? record[dataIndex].toString().toLowerCase().includes(value.toLowerCase())
: '',
onFilterDropdownVisibleChange: visible => {
if (visible) {
setTimeout(() => this.searchInput.select(), 100);
}
},
render: text =>
this.state.searchedColumn === dataIndex ? (
<Highlighter
highlightStyle={{ backgroundColor: '#ffc069', padding: 0 }}
searchWords={[this.state.searchText]}
autoEscape
textToHighlight={text ? text.toString() : ''}
/>
) : (
text
),
});
handleSearch = (selectedKeys, confirm, dataIndex) => {
confirm();
this.setState({
searchText: selectedKeys[0],
searchedColumn: dataIndex,
});
};
handleReset = clearFilters => {
clearFilters();
this.setState({ searchText: '' });
};
render() {
const columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
width: '30%',
...this.getColumnSearchProps('name'),
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
width: '20%',
...this.getColumnSearchProps('age'),
},
{
title: 'Address',
dataIndex: 'address',
key: 'address',
...this.getColumnSearchProps('address'),
},
];
return <Table columns={columns} dataSource={data} />;
}
}
ReactDOM.render(<App />, document.getElementById('container'));
Code can also be found here: https://codesandbox.io/s/lq2it?file=/package.json
Yes, searchText and searchedColumn are states and should be declared with useState like so:
const [searchText, updateSearchText] = useState('default_state');
const [searchedColumn, updateSearchedColumn] = useState('default_state');
updateSearchText(some_value) is the equivalent for
this.setState({
searchText : some_value
})
and updateSearchedColumn is the same said above but for the state: searchedColumn

How to render only items searched from a list of <li> elements

I have a list of li elements that is being passed down to one of my components. These are being rendered in the component. I have a search bar in the same component. I want to be able to only render the items that match what is written down in the search bar. This is what my component looks like.
import React, {Component} from 'react'
import {NavLink} from 'react-router-dom'
import LikeBtn from './LikeBtn'
class SearchForm extends Component {
constructor(props) {
super(props)
this.state = {
search: '',
newList: []
}
}
handleChange = (e) => {
this.setState({
[e.target.name]: e.target.value,
})
}
render() {
let list = this.props.listProp.map(item => <li className="listItem" key={item.id}><NavLink style={{ color: 'white' }} to={`activities/${item.id}`}>{item.name}</NavLink><LikeBtn /></li>)
let newList = list.filter(item => item.innerText === this.state.search)
console.log(newList)
return (
<>
<input type="text" name='search' onChange={this.handleChange}/>
<ul>
{list}
</ul>
</>
)
}
}
export default SearchForm
I don't know how to get that filtered out so that I can render the items. I tried doing innerText but since I have a LikeBtn component in the li element my filter doesn't work. How else would I be able to implement this? Are there more efficient ways of doing this?
You need to filter your data and not grab something you've already rendered on screen.
render() {
let filteredList = this.state.search
? this.props.listProp.filter((item) =>
item.name.includes(this.state.search),
)
: this.props.listProp;
return (
<>
<input type="text" name="search" onChange={this.handleChange} />
<ul>
{filteredList.map((item) => (
<li className="listItem" key={item.id}>
<NavLink style={{ color: 'white' }} to={`activities/${item.id}`}>
{item.name}
</NavLink>
<LikeBtn />
</li>
))}
</ul>
</>
);
}
There is also a snippet below that you can run:
class SearchForm extends React.Component {
constructor(props) {
super(props);
this.state = {
search: '',
};
}
handleChange = (e) => {
this.setState({
[e.target.name]: e.target.value,
});
};
render() {
let filteredList = this.state.search
? this.props.listProp.filter((item) =>
item.name.toLowerCase().includes(this.state.search.toLowerCase())
)
: this.props.listProp;
return (
<React.Fragment>
<input type="search" name="search" autocomplete="off" onChange={this.handleChange} />
<ul>
{filteredList.map((item) => (
<li className="listItem" key={item.id}>
<a href={`activities/${item.id}`}>{item.name}</a>
<button>Like</button>
</li>
))}
</ul>
</React.Fragment>
);
}
}
const data = [
{
id: 1,
name: 'Congress, The',
},
{
id: 2,
name: 'Glen or Glenda',
},
{
id: 3,
name: "Don't Drink the Water",
},
{
id: 4,
name: 'Blind',
},
{
id: 5,
name: 'Sirocco',
},
{
id: 6,
name: 'Sunset Strip',
},
{
id: 7,
name: 'Better Living',
},
{
id: 8,
name: '4:44 Last Day on Earth',
},
{
id: 9,
name: 'The Heavy',
},
{
id: 10,
name: 'Dr. Who and the Daleks',
},
{
id: 11,
name: 'Legend of Hell House, The',
},
{
id: 12,
name: 'Exit Humanity',
},
{
id: 13,
name: 'Young in Heart, The',
},
{
id: 14,
name: 'Soul Kitchen',
},
{
id: 15,
name: 'Accattone',
},
];
ReactDOM.render(<SearchForm listProp={data} />, document.querySelector('#root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
You can move this part
let list = this.props.listProp.map(item => <li className="listItem" key={item.id}><NavLink style={{ color: 'white' }} to={`activities/${item.id}`}>{item.name}</NavLink><LikeBtn /></li>)
to the return and filter the listProp on handleChange.
You can refer to this codeSandbox here for a working sample

Categories