I am learning react.js and trying to implement basic add to cart functionality trying to keep it simple, just to understand the flow.
I have two cards, in one of them are listed the items, and after selecting the item and clicking the button the corresponding item should appear in the another card.
As far as I understand I need to somehow filter and check if the unique property has a match with the parameter, add to the card.
Please, find the Codesandbox link and the code below
import Box from "#mui/material/Box";
import Button from "#mui/material/Button";
import Card from "#mui/material/Card";
import CardContent from "#mui/material/CardContent";
import { styled } from "#mui/material/styles";
import React, { useState, useEffect } from "react";
const StyledBox = styled("div")({
paddingRight: "70px",
paddingLeft: "70px",
boxShadow: "rgba(100, 100, 111, 0.2) 0px 7px 29px 0px",
backgroundColor: "#FFFFFF",
border: "1px solid black",
minHeight: "200px"
});
const AddToCard = () => {
const [data, setData] = useState([]);
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/users")
.then((res) => res.json())
.then((result) => setData(result))
.catch((e) => console.log(e));
}, []);
const addToCardHandleClick = (id) => {
setData(
data.map((item: any) => {
const tempItem = data.find((item: any) => item === id);
if (!tempItem) return [...item, { id }];
})
);
};
return (
<Card sx={{ maxWidth: 700, minHeight: "300px" }}>
<CardContent>
<Box
sx={{
display: "flex",
gap: "30px",
justifyContent: "center",
overflow: "auto"
}}
>
<StyledBox sx={{ minWidth: "100px" }}></StyledBox>//The selected item should appear here after clicking
<Box
sx={{
alignSelf: "center",
gap: "10px"
}}
>
<Button
variant="outlined"
onClick={() => addToCardHandleClick(null)} //instead of null should be correct property
>
Move
</Button>
</Box>
<StyledBox>
{data &&
data.map((item) => (
<ul key={item.id} style={{ listStyle: "none" }}>
<li>{item.name}</li>
</ul>
))}
</StyledBox>
</Box>
</CardContent>
</Card>
);
};
export default AddToCard;
Any help will be appreciated
I try it in the code sandbox: https://codesandbox.io/s/imgmediacard-material-demo-forked-9nq22
I use item.selected to keep track of clicked element and once you click on move I drop the item.selected property and add item.moved = true instead...
If you need to make the moved items appear in the right order, you can either:
Add item.order = ++counter or item.order = Date.now() and sort all item.moved by item.order
Use a separete array for moved items
Related
I am using a Dropdown which is basically like below on many screens in the app. As you can see, on the screens I use this dropdown, the dropdown becomes 50 height when clicked and 20 when clicked again. On the screens where I use this dropdown, if the dropdown is on (height is 50), I want it to close when clicked somewhere other than the dropdown on the screen. Since I use the dropdown on so many screens, I only want to do this inside the dropdown component. How can I do that?
import React, { useState } from 'react';
import { View, TouchableOpacity } from 'react-native';
export default Dropdown = () => {
const [isOpen, setIsOpen] = useState(false);
return (
<View
style={{ backgroundColor: "grey" }}>
<TouchableOpacity
style={{ height: 20, width: 200 }}
onPress={() => setIsOpen(prev => !prev)}>
</TouchableOpacity>
<View style={{
display: isOpen ? 'flex' : 'none',
height: 50
}}>
</View>
</View>
);
};
How to close the custom dropdown from outside the dropdown component
You can use a modal to show the drawer contents. In this way, you can easily close the drawer on the screens where it is used.
import React, { useRef, useState } from 'react';
import { View, TouchableOpacity, Modal, TouchableWithoutFeedback, Text } from 'react-native';
export default Dropdown = () => {
const [isOpen, setIsOpen] = useState(false);
const [dropdownFrame, setDropdownFrame] = useState(null)
const dropdownRef = useRef()
const onPress = () => {
if (dropdownRef.current && dropdownRef.current.measure) {
dropdownRef.current.measure((fx, fy, width, height, x, y) => {
// required dropdown measure to display content directly below the dropdown
setDropdownFrame({ width, height, x, y })
setIsOpen(true)
})
}
}
return (
<View>
{/* drawer title */}
<TouchableOpacity
style={{ height: 30, backgroundColor: "grey" }}
onPress={onPress}
ref={dropdownRef}
>
<Text>Drawer Title</Text>
</TouchableOpacity>
{/* drawer content */}
{isOpen &&
<Modal transparent>
<TouchableWithoutFeedback onPress={() => setIsOpen(false)}>
<View style={{ flexGrow: 1 }}>
<View style={{
height: 40,
backgroundColor: "red",
top: dropdownFrame.y + dropdownFrame.height,
left: dropdownFrame.x,
}} >
<Text>item</Text>
</View>
</View>
</TouchableWithoutFeedback>
</Modal>}
</View>
);
};
I have made this to-do list App using redux with the help of a tutorial for the sake of learning Redux architecture.
so the check box should work for every individual task added, but onChange I get all of the boxes' values turning to true when I figured I wasn't mapping throw the state.
but then if you look into the logger on the console you see that I have got it working, only failing to display the check properly.
you can find the full code here
this is the Task component
const Todo = ({ todo , idx , deleteTodo , editTodo,selected,text,check,checkTodo }) => {
return (
<div style={{ display: 'flex', flexDirection: 'flex-start', justifyContent: 'space-evenly', border: '1px solid white', padding: '5px', width: '200px', cursor: 'pointer' }} >
<input style={{ width: '1rem', height: '0.8rem', margin: '0', padding: '0' }} type="checkbox" checked={check} onChange={() => checkTodo(idx)} />
<div onClick={() => editTodo(idx)} >
{
selected === idx ? text : todo
} </div>
<div style={{ cursor: 'pointer' }} onClick={() => deleteTodo(idx)}>x</div>
</div>
)
}
Here is the action.reducer
export const checkTodo = key => ({
type: "CHECK_TODO",
payload: key
})
and this is the reducer switch case (the check box part )
case 'CHECK_TODO':
const todos5 = state.todos.map((todo, i) => {
if (i == action.payload) {
return { ...state, check: !todo.check ,text: todo}
}
return todo
})
setPersist(todos5)
return {
...state,todos5
}
https://github.com/Faycal88/Todo-App
I am fairly new to JavaScript and React, so I was hoping someone would be able to help me with something that seems fairly easy, I just don't fully grasp the concepts. I do not want to use ReactRouter, just manage it within the file.
I am creating a simple image slider using React and Material UI. However, I am having some difficulty with the functionality of the back button, preventing the app from crashing when going past the first image (into the negatives), and displaying the image number (ie: 3/5).
Here is my code:
import React, { useState } from "react";
import Button from "#material-ui/core/Button";
import MobileStepper from "#material-ui/core/MobileStepper";
import Paper from "#material-ui/core/Paper";
import KeyboardArrowRight from "#material-ui/icons/KeyboardArrowRight";
import Typography from "#material-ui/core/Typography";
import { useTheme } from "#material-ui/core/styles";
import KeyboardArrowLeft from "#material-ui/icons/KeyboardArrowLeft";
const myCollection = [
{
label: "First Picture",
imgPath:
"https://randompicturegenerator.com/img/dog-generator/g0ae914387a19ba58fc07fffe7f6952176159f445a3cd128c43ad59ae8b9baed35d8784cb3cdf4f4c16897571568d60c5_640.jpg",
},
{
label: "Second Picture",
imgPath:
"https://randompicturegenerator.com/img/dog-generator/g913a2b6f81253654df3b9e66abc189e6b966daf7a7a37b814b2336ab4459a832db90c8b923a5a8e28e63bb2fdd4496e1_640.jpg",
},
{
label: "Third Picture",
imgPath:
"https://randompicturegenerator.com/img/dog-generator/g5d33df79829d9cfbba39903471d1cd1bc2d6cdd243e222607e337d5575cb76ed10582deda8ed10bb0d1c3c43a6494f5a_640.jpg",
},
{
label: "Fourth Picture",
imgPath:
"https://randompicturegenerator.com/img/dog-generator/g446bd15f34f6fcb35272fa878493a90e9600b73e57f1f1dbca68ac22b9a1f780cb58c8a940d5727f930aa78d7b537b82_640.jpg",
},
{
label: "Fifth Picture",
imgPath:
"https://randompicturegenerator.com/img/dog-generator/g2efe8ff4951d16cc2d042d3714796204a975eb2c32fc5ee60d08956a729b7d5114b32cb0db6a5ba2e6d2948b8a6a0320_640.jpg",
},
];
const App = () => {
const CollectionSize = myCollection.length;
const theme = useTheme();
const [index, setIndex] = useState(0);
const goToNextPicture = () => {
setIndex((prevIndex) => prevIndex + 1);
};
const goToPrevPicture = () => {
setIndex((prevIndex) => prevIndex - 1);
};
return (
<>
<div style={{ marginLeft: "40%" }}>
<h2>Mans Best-Friend</h2>
<div style={{ maxWidth: 400, flexGrow: 1 }}>
<Paper
square
elevation={0}
style={{
height: 50,
display: "flex",
paddingLeft: theme.spacing(4),
backgroundColor: theme.palette.background.default,
alignItems: "center",
}}
>
<Typography>{myCollection[index].label}</Typography>
</Paper>
<img
src={myCollection[index].imgPath}
style={{
height: 255,
width: "100%",
maxWidth: 400,
display: "block",
overflow: "hidden",
}}
alt={myCollection[index].label}
/>
<MobileStepper
variant="text"
position="static"
index={index}
steps={CollectionSize}
nextButton={
<Button
size="small"
onClick={goToNextPicture}
disabled={index === CollectionSize - 1}
>
Next
{theme.direction !== "rtl" ? (
<KeyboardArrowRight />
) : (
<KeyboardArrowLeft />
)}
</Button>
}
/>
<MobileStepper
variant="text"
position="static"
index={index}
steps={CollectionSize}
backButton={
<Button
size="small"
onClick={goToPrevPicture}
disabled={index === CollectionSize + 1}
>
Back
{theme.direction !== "ltl" ? (
<KeyboardArrowRight />
) : (
<KeyboardArrowLeft />
)}
</Button>
}
/>
</div>
</div>
</>
);
};
export default App;
Error I am getting in the console:
const goToNextPicture = () => {
index<{your_array}.length-1?
setIndex((prevIndex) => prevIndex + 1):setIndex(0);
};
const goToPrevPicture = () => {
index>0
setIndex((prevIndex) => prevIndex - 1):setIndex(arr.length-1);
};
enter code here
I am working with the map function in react and I am getting all my info to map except for my button ref. Below is the code. The data is getting pulled from a database and passed as a prop as well. When I filter the list I get the look I am looking for but when I go to select a doctor I only get the last name for the doctor that was the last item iterated, and this is across all the buttons when I click them. Where am I going wrong if I may ask.
import {
Button,
Card,
CardActions,
CardContent,
Grid,
Typography,
} from "#material-ui/core";
import { makeStyles } from "#material-ui/styles";
import { useRef } from "react";
const styles = makeStyles({
bound: {
margin: "40px 200px",
position: "relative",
},
card: {
maxWidth: "70%",
margin: "50px auto",
borderStyle: "solid",
borderRadius: "50px",
borderColor: "black",
borderWidth: "3px",
"&:hover": {
boxShadow: "black",
borderColor: "green",
},
},
null: {
display: "none",
},
btn: {
margin: "20px 0 0 0",
borderRadius: "20px",
"&:hover": {
backgroundColor: "green",
color: "white",
},
},
});
const SearchResult = (props) => {
const doctorList = props.doctor;
const selectedRef = useRef();
const selectedHandler = (e) => {
console.log(e.target.target);
console.log(selectedRef.current.value);
};
const doctorCard = (doctorList) => {
return (
<Card
variant="outlined"
className={`${doctorList ? classes.card : classes.null}`}
key={doctorList.user_id}
id={doctorList.user_id}
>
<CardContent>
<Grid container>
<Grid xs={6} md={6} lg={6} item>
<img src="#" alt="Img.png" />
</Grid>
<Grid xs={6} md={6} lg={6} item>
<Typography variant="h6">
Dr. {doctorList.first_name} {doctorList.last_name}
</Typography>
<Typography variant="h6">
Speciality: {doctorList.title}
</Typography>
<Typography variant="h6">
Primary Language: {doctorList.primary_language}
</Typography>
</Grid>
</Grid>
<CardActions>
<Button
variant="contained"
className={classes.btn}
type="submit"
ref={selectedRef}
value={doctorList.last_name}
onClick={selectedHandler}
size="large"
fullWidth
>
Select
</Button>
</CardActions>
</CardContent>
</Card>
);
};
const classes = styles();
return (
<div className={classes.bound}>{doctorList.doctor.map(doctorCard)}</div>
);
};
export default SearchResult;
OUTPUT
You are looping list and using common reference 'selectedRef' in you list component. Once list rendering finishes, last_name of last item in your array is being stored in 'selectedRef' reference. That's why whenever you click it's always getting last item.
To resolve this you might want to break component on granular level. Use two component ItemList and ListItem. ItemList will simply loop array and in ListItem component create reference to each item while looping. That should resolve your issue.
const ItemList = (props) => {
const renderList=(doctor)=>{
return <ListItem key={`doctor-${doctor.id}`} {...doctor}/>
}
return <div>{props.data.map(renderList)}</div>
}
const ListItem = () => {
// reference below create new reference for every item
const selectedRef = useRef();
// rest of logic to render doctor card
}
I'm working on an application where I render the DB results by a loop through using the map method.
When the user clicks on each item, first it will add those id's into an array and toggle a CSS class to change the border color of a particular selected item, when the item is selected again, the border will disappear, same goes for all items rendered in the UI. Adding the id's into an array is working fine.
I'm facing a problem toggling CSS style to change the border color when click on each item.
Here is my code to render items on DOM
const [selectedItems, setSelectedItems] = React.useState([])
const onClickHandler = (id) => {
if (selectedItems.indexOf(id) > -1) {
let updatedItems = selectedItems.filter(itemID => itemID !== id)
console.log(updatedItems);
setSelectedItems(updatedItems)
} else {
setSelectedItems(prevState => [...prevState, id] )
}
setOpen(true);
}
{
courses.length > 0 && courses.map(course => {
return (
<>
<CourseItem course={course} onClickHandler={onClickHandler} itemSelect={itemSelect} />
</>
)
})
}
function CourseItem({ course, onClickHandler }) {
const classes = useStyles();
return (
<>
<Col style={{ marginTop: 10 }}>
<Paper elevation={0} className={classes.card}>
<div className={classes.cardTop} style={{ backgroundImage: `url('${course.thumbnail}')`, backgroundSize: 'cover', backgroundPosition: 'center' }}>
<div style={{ padding: '8px 18px', display: 'flex', justifyContent : 'space-between' }}>
<CheckCircleIcon onClick={() => onClickHandler(course._id)} />
{/* <Typography component={"label"} variant="caption"> { course.level }</Typography> */}
<Chip label={course.level} style={{ color: '#000', backgroundColor: '#f9f9f9', fontFamily: 'regular1', position: 'relative', zIndex: 0}} variant="outlined" />
</div>
</div>
<div className={classes.cardBottom}>
<Typography> { course.moduleName }</Typography>
<Typography variant="subtitle2" component={"span"}> { course.description }</Typography>
</div>
</Paper>
</Col>
</>
)
}
You can use tow different class for two different condition. Then use clsx to conditionally apply class.
clsx link -> npm clsx