I'm developing an application in React.JS
I need to put a condition before inserting.
The code:
const App = () => {
const [item, setItem] = useState([])
const [category, setCategory] = useState([])
const categories = category.map(elem => ({
value: elem.id,
label: elem.cat,
}));
const [category_select, setCategorySelect] = React.useState(null);
function handleChangeCategory(value) {
setCategorySelect(value);
}
useEffect(() => {
getItems()
}, [])
useEffect(() => {
getCategories()
}, [])
const getItems = async () => {
const response = await axios.get(REQUEST_1)
setItem(response.data)
}
const getCategories = async () => {
const response = await axios.get(REQUEST_2)
setCategory(response.data)
}
const addItems = () => {
axios.post(`${REQUEST_1}`, {cat: category_select.value});
};
const body = () => {
return item && item.map((elem,j) => {
return (
<tr key={elem.id}>
<td><span>{elem.cat}</span></td>
</tr>
)
})
}
return (
<>
<div>
<div>
<div>
<div>
<NoSsr>
<Select
classes={classes}
styles={selectStyles}
inputId="category"
TextFieldProps={{
label: 'Category',
InputLabelProps: {
htmlFor: 'category',
shrink: true,
},
placeholder: 'Category...',
}}
options={categories}
components={components}
value={category}
onChange={handleChangeCategory}
/>
</NoSsr>
<span>Select</span>
</div>
<div>
<label> </label>
<span onClick={addItems}></span>
</div>
</div>
<div>
<div>
<div>
<table>
<thead>
<tr>
<th>
<span>Category</span>
</th>
</tr>
</thead>
<tbody>
{body()}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</>
)
}
export default App;
The idea is to check if the item to be inserted already exists, and if it is already inserted, show a message through a popup.
How do I add this condition in the const addItems = () => {...} and the popup? Do I have to put that condition here?
How can I do it, suggestions?
You should check if the selected category already exists in addItems method.
You can use React packages like reactjs-popup to display a popup.
const [isShow, setIsShow] = useState(false)
const addItems = () => {
// check if selected category already exists
if (!category.some(elem => elem.cat === category_select.value)) {
axios.post(`${REQUEST_1}`, {cat: category_select.value});
}
else {
// update popup modal open state and display popup
setIsShow(true);
}
};
...
return (
...
{ isShow && <h1>Error</h1> }
...)
Related
Please help me! Delete Icon is not functional, when I click on delete icon it delete all the contact, on refreshing, it returns all the previous contacts. I am also using localStorage.
I have added all the Component of the React App Project.
App.js
import { v4 as uuid } from "uuid";
const App = () => {
const LOCAL_STORAGE_KEY = "contacts";
const [contacts, setContacts] = useState([]);
const addContactHandler = (contact) => {
console.log(contact);
setContacts([...contacts, { id: uuid(), ...contact }]);
};
const removeContactHandler = (id) => {
const newContactList = contacts.filter((contact) => {
return contact.id !== id;
});
setContacts(newContactList);
};
useEffect(() => {
const retrieveContacts = JSON.parse(
localStorage.getItem(LOCAL_STORAGE_KEY)
);
if (retrieveContacts) {
setContacts(retrieveContacts);
}
}, []);
useEffect(() => {
if (contacts.length) {
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(contacts));
}
}, [contacts]);
return (
<>
<div className="app">
<Header />
<AddContact addContactHandler={addContactHandler} />
<ContactList contacts={contacts} getContactId={removeContactHandler} />
</div>
</>
);
};
export default App;
ContactList.js
const ContactList = (props) => {
const deleteContactHandler = (id) => {
props.getContactId(id);
};
const renderContactList = props.contacts.map((contact) => {
return (
<>
<ContactCard
contact={contact}
clickHandler={deleteContactHandler}
key={contact.id}
/>
</>
);
});
return (
<>
<div className="contactList">
<h2 className="contactList__title">Contact List</h2>
<div className="contactList__container">
{renderContactList}
</div>
</div>
</>
);
};
ContactCard.js
const ContactCard = (props) => {
const { id, name, email } = props.contact;
return (
<>
<div className="contactCard">
<div className="contactCard__contact">
<img
className="contactCard__userIcon"
src={userIcon}
alt="user-icon"
/>
<div className="contactCard__userName">
<h2>{name}</h2>
<p>{email}</p>
</div>
</div>
<div className="contactCard__delIcon">
<img
src={delIcon}
alt="del-icon"
onClick={() => props.clickHandler(id)}
/>
</div>
</div>
</>
);
};
export default ContactCard;
I have researched out the references. Unable to get the Solution.
The effect to store the contacts do not save empty arrays.
Thats why you get the old array after refreshing your page.
Just remove the condition.
useEffect(() => {
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(contacts));
}, [contacts]);
But you should consider to remove this effect.
Save the contacts directly after setting the state instead.
const addContactHandler = (contact) => {
console.log(contact);
const newContactList = [...contacts, { id: uuid(), ...contact }];
setContacts(newContactList);
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(newContactList));
};
const removeContactHandler = (id) => {
const newContactList = contacts.filter((contact) => {
return contact.id !== id;
});
setContacts(newContactList);
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(newContactList));
};
I have this table with select menu:
export interface IActivePairsProps extends StateProps, DispatchProps, RouteComponentProps<{ url: string }> {}
export const ActivePairs = (props: IActivePairsProps) => {
const [paginationState, setPaginationState] = useState(
overridePaginationStateWithQueryParams(getSortState(props.location, ITEMS_PER_PAGE, 'id'), props.location.search)
);
const [exchangeId, setExchangeId] = useState('');
const getAllEntities = () => {
props.getEntities(paginationState.activePage - 1, paginationState.itemsPerPage, `${paginationState.sort},${paginationState.order}`);
props.getExchangesList();
};
const sortEntities = () => {
getAllEntities();
const endURL = `?page=${paginationState.activePage}&sort=${paginationState.sort},${paginationState.order}&exchangeId=${exchangeId}`;
if (props.location.search !== endURL) {
props.history.push(`${props.location.pathname}${endURL}`);
}
};
useEffect(() => {
sortEntities();
}, [paginationState.activePage, paginationState.order, paginationState.sort]);
useEffect(() => {
const params = new URLSearchParams(props.location.search);
const page = params.get('page');
const sort = params.get('sort');
if (page && sort) {
const sortSplit = sort.split(',');
setPaginationState({
...paginationState,
activePage: +page,
sort: sortSplit[0],
order: sortSplit[1],
});
}
const exchangeId = params.get('exchangeId');
}, [props.location.search]);
const sort = p => () => {
setPaginationState({
...paginationState,
order: paginationState.order === 'asc' ? 'desc' : 'asc',
sort: p,
});
};
const handlePagination = currentPage =>
setPaginationState({
...paginationState,
activePage: currentPage,
});
const handleSyncList = () => {
sortEntities();
};
const { activePairsList, exchangesList, match, loading, totalItems } = props;
return (
<div>
<div className="table-responsive">
{activePairsList && activePairsList.length > 0 ? (
<Table responsive>
<thead>
<tr>
.....
<select onChange={e => setExchangeId(e.target.value)}>
{exchangesList
? exchangesList.map(otherEntity => (
<option value={otherEntity.exchangeId} key={otherEntity.exchangeId}>
{otherEntity.exchangeLongName} - {otherEntity.exchangeId}
</option>
))
: null}
</select>
.........
</Table>
) : (
!loading && <div className="alert alert-warning">No Active Pairs found</div>
)}
</div>
{props.totalItems ? (
<div className={activePairsList && activePairsList.length > 0 ? '' : 'd-none'}>
<Row className="justify-content-center">
<JhiItemCount page={paginationState.activePage} total={totalItems} itemsPerPage={paginationState.itemsPerPage} />
</Row>
<Row className="justify-content-center">
<JhiPagination
activePage={paginationState.activePage}
onSelect={handlePagination}
maxButtons={5}
itemsPerPage={paginationState.itemsPerPage}
totalItems={props.totalItems}
/>
</Row>
</div>
) : (
''
)}
</div>
);
};
const mapStateToProps = ({ activePairs, exchangesList }: IRootState) => ({
activePairsList: activePairs.entities,
exchangesList: exchangesList.entities,
loading: activePairs.loading,
totalItems: activePairs.totalItems,
});
const mapDispatchToProps = {
getEntities,
getExchangesList,
};
type StateProps = ReturnType<typeof mapStateToProps>;
type DispatchProps = typeof mapDispatchToProps;
export default connect(mapStateToProps, mapDispatchToProps)(ActivePairs);
How I can reload the table data when I change the select menu item? I would like to reload the data from the table data with the new selected exchageId param.
useEffect(fn, deps);
As we can see in the React documentation, the way we use the effect hook looks like this:
,fn is the effectful function, and deps is an array of values it depends on. Every time the component renders, React checks if all the values in the deps array are still the same. If any of them has changed since the last render, fn is run again.,All right, so far all the examples exhibit the same behavior. The effect simply doesn't run again if the dependency value doesn't change.
So you only need to give the useEffect hook exchageId as deps and the component's loading function as fn, then UseEffect will rerenders your component.
I have one button in front each list item when I click on the watched button, I want the text of the button to be changed to not watched(or when click on not watched it change to watched) and to be included in the watched list. At the top, I have three buttons, watched and not watched , which one I clicked on. Show me the list of the movies that I changed, their state and for third button(with text of all )it shows the whole list.I think that my problem is handleWatchedBtn function . this is picture of project maybe it is simple my explanation! thank you for your help.
import React, { useEffect, useState } from "react";
const App = () => {
const [Movies, setMovie] = useState([]);
const [Loading, setLoading] = useState(true);
const [Keyword, setKeyword] = useState("");
const [OverSeven, setOverSeven] = useState(false);
const [filterByWatch, setfilterByWatch] = useState("ALL");
useEffect(() => {
fetch("http://my-json-server.typicode.com/bemaxima/fake-api/movies")
.then((response) => response.json())
.then((response) => {
setMovie(
response.map((item) => ({
id: item.id,
name: item.name,
rate: item.rate,
watched: false,
}))
);
setLoading(false);
});
}, []);
function handleWatchedBtn(id) {
setMovie(() =>
Movies.map((movie) => {
if (movie.id === id) {
return { movie, watched: !movie.watched };
}
return movie;
})
);
}
function handleWatchedChange(filter) {
setfilterByWatch({ filterByWatch: filter });
}
function handleKeywordChange(e) {
setKeyword(e.target.value);
}
function handleOverSevenChange(e) {
setOverSeven(e.target.checked);
}
function filterItems() {
return Movies.filter((item) =>
item.name.toLowerCase().includes(Keyword.toLowerCase())
)
.filter((item) => (OverSeven ? item.rate > 7 : true))
.filter((item) =>
filterByWatch === "ALL"
? true
: item.watched === (filterByWatch === "WATCHED")
);
}
if (Loading) {
return "Please wait...";
}
return (
<div>
<div>
<div>
Keyword
<input type="text" value={Keyword} onChange={handleKeywordChange} />
</div>
<div>
<button onClick={() => handleWatchedChange("ALL")}>all</button>
<button onClick={() => handleWatchedChange("WATCHED")}>watch</button>
<button onClick={() => handleWatchedChange("NOT_WATCHED")}>
not watch
</button>
</div>
<div>
Only over 7.0
<input
type="checkbox"
checked={OverSeven}
onChange={handleOverSevenChange}
/>
</div>
<div>
<ul>
{filterItems().map((movie) => (
<li data-id={movie.id}>
{`${movie.name} ${movie.rate}`}{" "}
<button onClick={() => handleWatchedBtn(movie.id)}>
{movie.watched ? "Watched" : " Not watched"}
</button>
</li>
))}
</ul>
</div>
</div>
</div>
);
};
export default App;
You could introduce a new array state which stores all filtered movies and is updated every time the movies or the filter are updated.
You can then pass its reference to the map function that generates the list.
Also, notice that I've added the spread operator (...) in the handleWatchedBtn function and adjusted the handleWatchedChange method to update the state to a string and not an object.
Try to change your code like this:
import React, { useEffect, useState } from "react";
const App = () => {
const [Movies, setMovie] = useState([]);
const [filteredMovies, setFilteredMovies] = useState([]);
const [Loading, setLoading] = useState(true);
const [Keyword, setKeyword] = useState("");
const [OverSeven, setOverSeven] = useState(false);
const [filterByWatch, setfilterByWatch] = useState("ALL");
useEffect(() => {
fetch("http://my-json-server.typicode.com/bemaxima/fake-api/movies")
.then((response) => response.json())
.then((response) => {
const newMovies = response.map((item) => ({
id: item.id,
name: item.name,
rate: item.rate,
watched: false,
}));
setMovie(newMovies);
setLoading(false);
});
}, []);
useEffect(() => {
// Update filtered movies when the data or the filter changes
const newFilteredMovies = Movies.filter((item) =>
item.name.toLowerCase().includes(Keyword.toLowerCase())
)
.filter((item) => (OverSeven ? item.rate > 7 : true))
.filter((item) =>
filterByWatch === "ALL"
? true
: item.watched === (filterByWatch === "WATCHED")
);
setFilteredMovies(newFilteredMovies);
}, [Movies, filterByWatch]);
function handleWatchedBtn(id) {
setMovie(() =>
Movies.map((movie) => {
if (movie.id === id) {
// Add the spread operator here
return { ...movie, watched: !movie.watched };
}
return movie;
})
);
}
function handleWatchedChange(filter) {
// Change this line
setfilterByWatch(filter);
}
function handleKeywordChange(e) {
setKeyword(e.target.value);
}
function handleOverSevenChange(e) {
setOverSeven(e.target.checked);
}
if (Loading) {
return "Please wait...";
}
return (
<div>
<div>
<div>
Keyword
<input type="text" value={Keyword} onChange={handleKeywordChange} />
</div>
<div>
<button onClick={() => handleWatchedChange("ALL")}>all</button>
<button onClick={() => handleWatchedChange("WATCHED")}>watch</button>
<button onClick={() => handleWatchedChange("NOT_WATCHED")}>
not watch
</button>
</div>
<div>
Only over 7.0
<input
type="checkbox"
checked={OverSeven}
onChange={handleOverSevenChange}
/>
</div>
<div>
<ul>
{filteredMovies.map((movie) => (
<li data-id={movie.id}>
{`${movie.name} ${movie.rate}`}{" "}
<button onClick={() => handleWatchedBtn(movie.id)}>
{movie.watched ? "Watched" : " Not watched"}
</button>
</li>
))}
</ul>
</div>
</div>
</div>
);
};
export default App;
I'm developing an application in react.Js
And I have a table that I need to update without having to refresh the page.
The code:
const App = () => {
const [item, setItem] = useState([])
const [category, setCategory] = useState([])
const categories = category.map(elem => ({
value: elem.id,
label: elem.cat,
}));
const [category_select, setCategorySelect] = React.useState(null);
function handleChangeCategory(value) {
setCategorySelect(value);
}
useEffect(() => {
getItems()
}, [])
useEffect(() => {
getCategories()
}, [])
const getItems = async () => {
const response = await axios.get(REQUEST_1)
setItem(response.data)
}
const getCategories = async () => {
const response = await axios.get(REQUEST_2)
setCategory(response.data)
}
const addItems = () => {
axios.post(`${REQUEST_1}`, {cat: category_select.value});
};
const body = () => {
return item && item.map((elem,j) => {
return (
<tr key={elem.id}>
<td><span>{elem.cat}</span></td>
</tr>
)
})
}
return (
<>
<div>
<div>
<div>
<div>
<NoSsr>
<Select
classes={classes}
styles={selectStyles}
inputId="category"
TextFieldProps={{
label: 'Category',
InputLabelProps: {
htmlFor: 'category',
shrink: true,
},
placeholder: 'Search',
}}
options={categories}
components={components}
value={category}
onChange={handleChangeCategory}
/>
</NoSsr>
<span>Select</span>
</div>
<div>
<label> </label>
<span onClick={addItems}></span>
</div>
</div>
<div>
<div>
<div>
<table>
<thead>
<tr>
<th>
<span>Category</span>
</th>
</tr>
</thead>
<tbody>
{body()}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</>
)
}
export default App;
The idea is that a category is selected and the item is added.
What I need is to be able to update the map (return item && item.map((elem,j) => {...})) after clicking on <span onClick={addItems}></span> which calls the addItems function that adds the item. Simply put I need it to be added in the table without having to update.
How can I do it, suggestions?
You need to add the dependency to the useEffect so that every time the user wants adds an item, getItems() request is called to get the new items. So, your code might look like something like this:
const App = () => {
const [item, setItem] = useState([])
const [category, setCategory] = useState([])
const [addedItem, setAddedItem] = useState('')
const categories = category.map(elem => ({
value: elem.id,
label: elem.cat,
}));
const [categorySelect, setCategorySelect] = React.useState(null);
function handleChangeCategory(value) {
setCategorySelect(value);
}
useEffect(() => {
getItems()
}, [addedItem])
useEffect(() => {
getCategories()
}, [])
const getItems = async () => {
const response = await axios.get(REQUEST_1)
setItem(response.data)
}
const getCategories = async () => {
const response = await axios.get(REQUEST_2)
setCategory(response.data)
}
const addItems = () => {
const response = await axios.post(`${REQUEST_1}`, {cat: category_select.value});
setAddedItem(response.data)
};
const body = () => {
return item && item.map((elem,j) => {
return (
<tr key={elem.id}>
<td><span>{elem.cat}</span></td>
</tr>
)
})
}
return (
<>
<div>
<div>
<div>
<div>
<NoSsr>
<Select
classes={classes}
styles={selectStyles}
inputId="category"
TextFieldProps={{
label: 'Category',
InputLabelProps: {
htmlFor: 'category',
shrink: true,
},
placeholder: 'Search',
}}
options={categories}
components={components}
value={category}
onChange={handleChangeCategory}
/>
</NoSsr>
<span>Select</span>
</div>
<div>
<label> </label>
<span onClick={addItems}></span>
</div>
</div>
<div>
<div>
<div>
<table>
<thead>
<tr>
<th>
<span>Category</span>
</th>
</tr>
</thead>
<tbody>
{body()}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</>
)
}
export default App;
I try to update the state for the input, but the input is losing focus on every key-strike. So i can only enter one character at time. I guess it has something todo with the rerender of the component?
The state updates but only one character at time.
import React, { useState } from 'react'
const InitialState = [
{
Id: 1,
Server: 'server-1',
IpAdress: '151.222.33.2'
},
{
Id: 2,
Server: 'server-2',
IpAdress: '152.332.22.1'
}
]
const Grid = props => {
const [data, setData] = useState(InitialState)
const getKeys = () => {
return Object.keys(data[0])
}
const renderHeader = () => {
return (
<tr>
{getKeys().map(key => {
return <th>{key}</th>
})}
</tr>
)
}
const getRowsData = () => {
var items = data
var keys = getKeys()
return items.map((row, index) => {
return (
<tr key={index} data-row={index}>
<RenderRow key={index} data={row} keys={keys} rowIndex={index} />
</tr>
)
})
}
const RenderRow = props => {
console.log()
return props.keys.map((key, index) => {
return (
<td>
<input
data-index={index}
data-key={key}
data-row={props.rowIndex}
value={data[props.rowIndex][key]}
type='text'
onChange={event => updateData(props, event, props.rowIndex, key)}
/>
</td>
)
})
}
const updateData = (props, event, rowIndex, key) => {
let update = [...data]
update[rowIndex][key] = event.target.value
setData(update)
}
return (
<table>
{renderHeader()}
{getRowsData()}
</table>
)
}
export default Grid
Anyone have an idea whats wrong in my code?
Thanks!!
change the way you renderRow instead define as a component, define that as a render function instead then it will solve your issue. take a look a my codesanbox here https://codesandbox.io/s/kind-snyder-pjqqk
const renderRow = props => {
return props.keys.map((key, index) => {
return (
<td>
<input
data-index={index}
data-key={key}
data-row={props.rowIndex}
value={data[props.rowIndex][key]}
type="text"
onChange={event => updateData(props, event, props.rowIndex, key)}
/>
</td>
);
});
};
const getRowsData = () => {
var items = data;
var keys = getKeys();
return items.map((row, index) => {
return (
<tr key={index} data-row={index}>
{renderRow({ data: row, keys, rowIndex: index })}
</tr>
);
});
};
you can refer to this question Why does React discard the entire DOM subtree and recreate it from scratch? if you want to learn more about the reason
You are creating new RenderRow component every time Grid renders. You should define it outside your component.