I've seen many answers to this question but I can't make it work for me, I'm trying to have an Add to Menu button for each recipe that I have, and what happens now is that on the first click it creates an empty array, then the second time it works.
const [selectedItems, setSelectedItems] = useState([]);
const handleClick = (e,selectedItem) => {
let newState = [...selectedItems,selectedItem]
setSelectedItems(newState);
console.log(selectedItems)
}
...
...
...
{recipes.reduce(reduceRecipes, []).map((item, index) => (
<Carousel.Item key={item._id}>
<div className="d-flex justify-content-center">
<Row>
{item.map((item, index) => {
return (
<Col sm>
<Card key={item._id} style={{ width: "18rem" }}>
<Card.Img variant="top" src={item.photo_location} />
<Card.Body>
<div className="title-container">
<Card.Title>
{item.name}
</Card.Title>
<p><FcAlarmClock/> {item.prep_time + item.cook_time} minutes</p>
</div>
<Button variant='warning' onClick={(e) => {handleClick(e,item)}}>Add to Menu</Button>
</Card.Body>
</Card>
</Col>
);
})}
</Row>
</div>
</Carousel.Item>
))}
The update will be reflected in the next render. That's how react works by design.
Take your example:
const handleClick = (e,selectedItem) => {
console.log(selectedItems) // current state
let newState = [...selectedItems,selectedItem]
setSelectedItems(newState);
console.log(selectedItems) // This won't print the recently updated state, but the same it printed at the beginning of the function
}
Check this: https://stackoverflow.com/a/54069332/4898348
It's unclear why you need selectedItems to have the updated state right there. You can just simply use newState instead.
Related
I'm trying to save data on localStorage if the user clicked on a like button and remove it if the user unclicked it. This is my code
const [menu, setMenu] = useState([]);
const [iconStates, setIconStates] = useState({});
...
...
...
{recipes.reduce(reduceRecipes, []).map((item, index) => (
<Carousel.Item key={item._id}>
<div className="d-flex justify-content-center">
<Row>
{item.map((item, index) => {
return (
<Col sm>
<Card key={item._id} style={{ width: "18rem" }}>
<Card.Img variant="top" src={item.photo_location} />
<Card.Body>
<div className="title-container">
<Card.Title>
{item.name}
<RiHeart3Fill
className={
iconStates[item._id] ? 'heart active' : 'heart'
}
onClick={() => {
if (menu.includes(item)) {
setMenu(menu.filter(menuItem => menuItem._id !== item._id));
} else {
setMenu(menu.concat(item));
}
localStorage.setItem('menu', JSON.stringify(menu));
setIconStates({...iconStates, [item._id]: !iconStates[item._id]})
}}
/>
I Codesandbox version is here.
It doesn't work as it supposed to do now, when I click the icon on one item it doesn't get added until I click it again, and if I unclicked any item everything in the LocalStorage is deleted. can anyone elaborate on how to fix this?
I am using axios to return data from an API and trying to present this in to various nested components in my React App.
The code looks something like this:
const Building = () => {
const { bid } = useParams();
const { userAccessToken } = useAuth();
const [buildingData, setBuildingData] = useState([]);
const bearerToken = `Bearer ${userAccessToken}`;
React.useEffect(() => {
let isMounted = true;
const axiosConfig = {
headers: { Authorization: bearerToken },
};
axios
.get(
"http://localhost:3001/building?requestedlid=2&requestedbid=" + bid,
axiosConfig
)
.then(function (response) {
if (isMounted) {
setBuildingData(response.data[0]);
}
})
.catch(function (error) {
// handle error
console.log(error);
});
return () => {
isMounted = false;
};
}, [bearerToken, bid]);
return (
<React.Fragment>
<Helmet title="Building Profile" />
<Container fluid className="p-0">
<Breadcrumb className="float-end mt-2">
<Breadcrumb.Item href="/dashboard/default">Home</Breadcrumb.Item>
<Breadcrumb.Item href="/buildings/portfolio">
Portfolio
</Breadcrumb.Item>
<Breadcrumb.Item active>Building Profile</Breadcrumb.Item>
</Breadcrumb>
<h1 className="h3 mb-3">
Building Profile
<OffcanvasHelp
id="buildingprofile"
name="Building Profile"
scroll
backdrop
/>
</h1>
<div className="clearfix"></div>
<Row>
<Col xl="8">
<BuildingProfile
name={buildingData.building_name}
status={buildingData.status}
description={buildingData.description}
keycontacts={buildingData.key_contacts}
created={buildingData.stats.created_date}
golive={buildingData.stats.golive_date}
/>
<Rooms />
</Col>
<Col xl="4">
<AccountManager />
<Map location={buildingData.location} />
<GetSupport type="commercial" />
</Col>
</Row>
</Container>
</React.Fragment>
);
};
My problem is I am receiving the common error:
Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
Using the methods described in many StackOverflow answers, to check an isMounted boolean as in my code above, I am unable to resolve the issue.
Weirdly, the issue specifically occurs when I am passing these two props to the BuildingProfile component:
created={buildingData.stats.created_date}
golive={buildingData.stats.golive_date}
If I don't pass these two props, everything else works fine.
I'm sure I'm missing something silly but after several hours of trying to figure it out I'm still stuck. Anybody who can provide a pointer or any tips, I would be really grateful.
Many thanks
--- Update - including the BuildingProfile component:
const BuildingProfile = ({
name,
status,
description,
created,
golive,
keycontacts,
}) => {
// Modal config for "Deactivate Building"
const initOpenModals = () => {
let modals = {};
colors.forEach((color, index) => {
modals = Object.assign({}, modals, { [index]: false });
});
console.log(modals);
return modals;
};
const [openModals, setOpenModals] = useState(() => initOpenModals());
const toggle = (index) => {
// Toggle selected element
setOpenModals((openModals) =>
Object.assign({}, openModals, { [index]: !openModals[index] })
);
};
const notyf = useContext(NotyfContext);
const [type] = useState("success");
const [duration] = useState("5000");
const [ripple] = useState(true);
const [dismissible] = useState(false);
const [positionX] = useState("right");
const [positionY] = useState("top");
const navigate = useNavigate();
return (
<Card>
<Card.Header className="mb-0 pb-0">
<Card.Title className="mb-0">
<IsAllowed to="edit:buildings">
<div className="card-actions float-end">
<Dropdown align="end">
<Dropdown.Toggle as="a" bsPrefix="-">
<MoreHorizontal />
</Dropdown.Toggle>
<Dropdown.Menu>
<Dropdown.Item onClick={() => navigate("/buildings/edit")}>
Edit Building
</Dropdown.Item>
<React.Fragment key="deactivateBuilding">
<Dropdown.Item onClick={() => toggle("deactivateBuilding")}>
Deactivate Building
</Dropdown.Item>
<Modal
show={openModals["deactivateBuilding"]}
onHide={() => toggle("deactivateBuilding")}
centered
>
<Modal.Header closeButton>
<b>Admin Function:</b> Deactivate Building
</Modal.Header>
<Modal.Body className="m-3">
<p className="text-left mb-0">
Are you sure you want to deactivate the
<b>Bus Works</b> building? This will prevent the
building from showing up in the platform completely.
</p>
</Modal.Body>
<Modal.Footer>
<Button
variant="secondary"
onClick={() => toggle("deactivateBuilding")}
>
Close
</Button>{" "}
<Button
variant="danger"
onClick={() => {
toggle("deactivateBuilding");
notyf.open({
type,
message: "The building has been deactivated.",
duration,
ripple,
dismissible,
position: {
x: positionX,
y: positionY,
},
});
}}
>
Deactivate Building
</Button>
</Modal.Footer>
</Modal>
</React.Fragment>
</Dropdown.Menu>
</Dropdown>
</div>
</IsAllowed>
<h1 className="mb-0 pb-0">{name}</h1>
<Badge
className={
status === "Live Building"
? "my-2 btn-gradient inline"
: "my-2 inline"
}
bg="success"
>
{status}
</Badge>
</Card.Title>
</Card.Header>
<Card.Body>
<h5>Building Overview:</h5>
<p className="mb-4">{description}</p>
<div className="row">
<div className="col-md-4">
<div className="mb-4">
<h5>Created Date</h5>
<p>{created}</p>
</div>
</div>
<div className="col-md-4">
<div className="mb-4">
<h5>Go-Live Date</h5>
<p>{golive}</p>
</div>
</div>
<div className="col-md-4">
<div className="mb-4">
<h5>Key Contacts</h5>
<div>
<span className="me-1">
<OverlayTrigger
placement="right"
overlay={
<Tooltip id="tooltip-right">Joe Bloggs</Tooltip>
}
>
<img
src={avatar3}
width="28"
height="28"
className="rounded-circle me-2"
alt="Joe Bloggs"
/>
</OverlayTrigger>
</span>
<span className="me-1">
<OverlayTrigger
placement="right"
overlay={
<Tooltip id="tooltip-right">Joe Bloggs</Tooltip>
}
>
<img
src={avatar2}
width="28"
height="28"
className="rounded-circle me-2"
alt="Joe Bloggs"
/>
</OverlayTrigger>
</span>
<span className="me-1">
<OverlayTrigger
placement="right"
overlay={
<Tooltip id="tooltip-right">Joe Bloggs</Tooltip>
}
>
<img
src={avatar1}
width="28"
height="28"
className="rounded-circle me-2"
alt="Joe Bloggs"
/>
</OverlayTrigger>
</span>
<Link to="#" className="d-inline-block text-muted fw-bold ms-2">
+2 more
</Link>
</div>
</div>
</div>
</div>
</Card.Body>
</Card>
);
};
I figured this out and wanted to share my answer in-case it helps anyone else.
It turns out that because I am trying to access the nested object property in the rendered component before the API call has finished, the property ("stats") doesn't exist. When the state eventually updates once the API call has finished, it cannot update the component resulting in the error I was seeing.
The way to fix this is to do something like this:
.... // useState should be set to an object, not an array
const [buildingData, setBuildingData] = useState({});
.... // skipping past intermediary code for brevity
.then(function (response) {
if (isMounted) {
setBuildingData({
name: response.data[0].building_name,
status: response.data[0].status,
description: response.data[0].description,
keycontacts: response.data[0].key_contacts,
created: response.data[0].stats.created_date,
golive: response.data[0].stats.golive_date
});
}
})
.... // then to access it in the component use:
<BuildingProfile
name={buildingData.name}
status={buildingData.status}
description={buildingData.description}
keycontacts={buildingData.keycontacts}
created={buildingData.created}
golive={buildingData.golive}
/>
Doing it this way means that the nested object is updated with API data inside of the useEffect hook, not in the component itself.
Hope this helps somebody.
I don't understand how I can add an item on localStroge with the handle change method in react js.
The problem is I want to add a favorite list. When I click the favorite list all checkbox fill I need to control them.
I want to store the filled item in my local storage. handlechange function fill all favorite icon why?
Every click will be a single item fill. Is it possible to Material UI or Martial UI checkBox icon? How can I handle it?
Here is my UI view
function Main() {
// Here is my state define
const [checked, setChecked] = React.useState(
localStorage.getItem("love") === "true"
);
const handleChange = (e) => {
localStorage.setItem("love", `${e.target.checked}`);
setChecked(e.target.checked);
console.log(e.target.checked);
};
return (
<>
<div className="row mt-5">
{isLoading ? (
<>
{Array.from({ length }).map((_, i) => (
<MainLoading key={i} />
))}
</>
) : error ? (
<p>Error occured</p>
) : (
<>
{data?.map((product) => (
<div className="col-md-3 mb-5 text-center" key={product.id}>
<img
className="w-100"
style={{ height: "200px", objectFit: "contain" }}
src={product.image}
alt=""
/>
<div>{product.title.substring(0, 20)}</div>
<button
onClick={() => handelAddTocard(product)}
className="mt-3"
>
Add to card
</button>
<button>
<Link
to={`/details/${product.id}`}
className="mt-3 text-decoration-none text-black"
>
view details
</Link>
</button>
{/* How can i control evey single item */}
<Checkbox
checked={checked}
onChange={handleChange}
icon={<FavoriteBorder />}
checkedIcon={<Favorite />}
/>
</div>
))}
</>
)}
</div>
</>
);
}
export default Main;
The problem is that you are using a boolean and you have no way to identify a specific item.
If you want to favorite multiple items, I would use something like this:
const [checked, setChecked] = React.useState(
JSON.parse(localStorage.getItem("loveIds") || "[]")
);
const handleCheck = (id, productChecked) => {
const newItems = productChecked ? [...checked, id] : checked.filter(x => x !== id);
localStorage.setItem("loveIds", JSON.stringify(newItemS));
setChecked(newItems);
console.log(newItems);
};
// ...
<Checkbox
checked={checked}
onChange={(e) => handleCheck(product.id, e.target.checked)}
icon={<FavoriteBorder />}
checkedIcon={<Favorite />}
/>
I'm trying to make a list of components. I need to remove the items individually but it seems that the state inside the remove function is always outdated.
For example, if I add 10 authors and click in the 10th author remove button, it'll show me 9 elements (which is already wrong) and if I click on the 2nd author, it shows me just 1 element inside the array. Am I missing something here?
const [authorsFields, setAuthorsFields] = useState<Array<JSX.Element>>([]);
const removeAuthorField = () => {
console.log(authorsFields.length);
}
const removeButton = () => {
return (
<Col className={"d-flex justify-content-end py-1"}>
<Button variant={"danger"} onClick={() => removeAuthorField()}>Remove author</Button>
</Col>
);
}
const authorField = (removable: boolean) => {
return (
<>
<Row className={"mb-2"}>
<Form.Group className={"py-1"}>
<Form.Label>Author name</Form.Label>
<Form.Control type={"text"}/>
</Form.Group>
{removable && removeButton()}
</Row>
</>
);
}
const addAuthorField = () => {
if (authorsFields.length !== 0) {
setAuthorsFields((old) => [...old, authorField(true)]);
} else {
setAuthorsFields([authorField(false)]);
}
}
useEffect(() => {
if (authorsFields.length === 0) {
addAuthorField();
}
}, [])
return (
<>
<Col sm={3} style={{maxHeight: "60vh"}} className={"mt-4"}>
<Row>
{authorsFields}
<Row>
<Form.Group className={"py-1"}>
<Button style={{width: "100%"}} onClick={() => addAuthorField()}>
Add Author
</Button>
</Form.Group>
</Row>
</Row>
</Col>
</>
);
Use the following functional component as an example to modify your code on how to use JSX elements seperated from the state management inside the functional components.
import React, { useState } from "react";
import { Button, Row, Col } from "antd";
function App() {
const [authorsCount, setAuthorsCount] = useState(0);
// Use authorsFields to manage authors details in an array of objects
const [authorsFields, setAuthorsFields] = useState([]);
const removeAuthorField = (id) => {
// To remove relevant author filter out the authors without the relevant id
setAuthorsFields((old) =>
old.filter((authorField) => authorField.id !== id)
);
};
const addAuthorField = () => {
setAuthorsFields((old) => [...old, { id: authorsCount, removable: true }]);
setAuthorsCount((old) => old + 1);
};
return (
<div>
<Col sm={3} style={{ maxHeight: "60vh" }} className={"mt-4"}>
<Row>
{authorsFields.map((authorField) => (
<Row className={"mb-2"}>
<div className={"py-1"}>
<div>{`Author name ${authorField.id}`}</div>
</div>
{authorField.removable && (
<>
<Col className={"d-flex justify-content-end py-1"}>
<Button
variant={"danger"}
onClick={() => removeAuthorField(authorField.id)}
>
Remove author
</Button>
</Col>
</>
)}
</Row>
))}
<Row>
<div className={"py-1"}>
<Button
style={{ width: "100%" }}
onClick={() => addAuthorField()}
>
Add Author
</Button>
</div>
</Row>
</Row>
</Col>
</div>
);
}
export default App;
Following is the view.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
I am new to react,
I am trying to append the html from the return function within the same component,
here is my code
// reactstrap components
import {useState} from "react"
import {
Badge,
Card,
CardHeader,
CardFooter,
Button,
UncontrolledTooltip,
} from "reactstrap";
// core components
//import { Link } from "react-router-dom";
import Header from "components/Headers/Header.js";
import 'react-toastify/dist/ReactToastify.css';
import Navbar from "components/Navbars/modulenavbar"
// const dropdownedit=(editid)=>{
// alert("clicked");
// console.log(editid.target.value);
// return dropdownedit;
// }npm install react-bootstrap-icons
import axios from "axios";
//import data from "./data";
import React from "react";
import { Link } from "react-router-dom";
var apitoken= localStorage.getItem('apitoken');
//const api=axios.create({baseURL:"https://api/v1/user/login",headers: {'Authorization ': apitoken}})
const api=axios.create({baseURL:"https://api/account"})
const options = {
headers: {'Authorization': apitoken}
}
var accountarry=[];
const Accounts = () => {
const [accounts, setaccount] = React.useState([]);
const [loading, setLoading] = React.useState(true);
const [disabled, setDisabled] = useState(false);
const [selectedStudent, setSelectedStudent] = useState({});
React.useEffect(async () => {
const response = await api.get("/",options);
accountarry=response.data.response
setaccount(accountarry);
setLoading(false);
}, []);
const handleScroll = e => {
let element = e.target
if (element.scrollHeight - element.scrollTop === element.clientHeight) {
// do something at end of scroll
console.log("End")
accountarry.push({"name":"arrayvalue"})
}}
const Save = (event) => {
alert("ok");
return<>
<Row>
<div className="col">
<FormGroup>
<Input
className="form-control-alternative"
placeholder="Comments"
rows="4"
id="input-notes"
defaultValue="dynamic append"
type="textarea"
disabled={disabled}
/>
</FormGroup>
</div>
<Col className="col-auto">
<UncontrolledDropdown>
<DropdownToggle
className="btn-icon-only text-light"
href="#pablo"
role="button"
size="sm"
color=""
onClick={(e) => e.preventDefault()}
>
<i className="fas fa-ellipsis-v" />
</DropdownToggle>
<DropdownMenu className="dropdown-menu-arrow" right>
{/* <DropdownItem value="9566412482" id="dropdown-menu-align-right" onClick={handleGameClick}> */}
{/* <DropdownItem value={id} id="dropdown-menu-align-right" to={`/admin/editcontact?id=${id}`} tag={Link}> */}
{/* </DropdownItem> */}
<DropdownItem
href="#pablo"
onClick={(e) => e.preventDefault()}
>
Delete
</DropdownItem>
</DropdownMenu>
</UncontrolledDropdown>
</Col>
</Row>
</>
}
const Handlestudentclick = (student) => {
setSelectedStudent(student)
}
if (loading) {
return <>Loading...</>;
}
return (
<>
<Header />
{/* Page content */}
<Container className="mt--0" fluid>
<Row>
<Col className="order-xl-1" xl="12">
<Card className="bg-secondary shadow">
<Row>
<Col xs="9">
<Card className="card-stats mb-4 mb-lg-0">
<CardBody>
<div>
<Save/>
<Row>
<div className="col">
<FormGroup>
<Input
className="form-control-alternative"
placeholder="Comments"
rows="4"
id="input-notes"
// defaultValue="A beautiful Dashboard for Bootstrap 4. It is Free and
// Open Source."
type="textarea"
// disabled={disabled}
/>
</FormGroup>
</div>
</Row>
<Row className="align-items-center">
<Col xs="8">
</Col>
<Col className="text-right" xs="4">
<Button
color="success"
href="#pablo"
onClick={save}
>
Save
</Button>
</Col>
</Row>
</div>
</CardBody>
</Card>
</Col>
</Row>
</Card>
</Col>
</Row>
</Container>
</>
)
}
const Display = () => {
return (
<>
<Accounts/>
</>
);
};
export default Display;
In the above code i have used button onclick={save} so the save function will run and return the html for append it in the same component below save, .
I dont know how to do this in react , can you help me of what i have done wrong here.
Thanks in advance,
The fact that your save function returns html has no effect right now, because you don't do anything with it. The clicked button executes a function and gets some html in return, it's useless :).
The actual, working flow could be:
Create a state variable that holds the additional html to render (or null if there's no additional html).
Modify your save function to, instead of returning the code, set this state variable accordingly.
Place this state variable within your returned html, so that setting it will result in actualy rendering the additional html.
The basic example is:
const [additional, setAdditional] = useState(null);
const save = () => {
const myAdditionalHtml = (
<div>additional</div>
);
setAdditional(myAdditionalHtml);
}
return (
<>
<div>my standard code
{additional}
<button onClick={() => save()}>save</button>
</>
);