How to do loop in json file in react - javascript

I need basic help.
I have an array. When i click some link on the screen, i want to see just their page, i mean him/her name, not all of them.
First, user page has all of the users. If i click some of them, i want to go her/his page. How can i do this?
Users has userid, so it may help you.
ProfilePage codes: (it gives all of the users with item.name)
const ProfilePage = () => {
const { posts, users } = useContext(UserContext);
return (
<>
{users.map((item, index) => {
return (
<>
<h1>{item.name}</h1>
</>
);
})}
</>
);
};
Users page codes:
return (
<>
{users.map((item, index) => {
return (
<>
<a href={`/profile/${item.username}`}>
<List className={classes.root}>
<ListItem alignItems="flex-start">
<ListItemAvatar>
<Avatar alt="Remy Sharp" />
</ListItemAvatar>
<ListItemText
primary={item.email}
secondary={
<React.Fragment>
<Typography
component="span"
variant="body2"
className={classes.inline}
color="textPrimary"
>
{item.name}
</Typography>
{<br />}
{item.username}
</React.Fragment>
}
/>
</ListItem>
<Divider variant="inset" component="li" />
</List>
</a>
</>
);
})}
</>
);
Users page image:
Profile page image: (first users page. It should show just 1 name not all of them, i know i used map its wrong)

const ProfilePage = () => {
const { posts, users } = useContext(UserContext);
const { name } = useParams();//Your dynamic route name. Example: <Route path="/profile/:name">
const filteredUsers = users.filter(item => item.name === name);//Find all users with name Bret
//Ideally, you need to make a router with a dynamic ID, then there will be a unique user. Example: <Route path="/profile/:id">
//const { id } = useParams();
//const filteredUsers = users.filter(item => item.id === id);
return (
<>
{filteredUsers.map((item, index) => {
return (
<>
<h1>{item.name}</h1>
</>
);
})}
</>
);
};

Related

Is rendering the Autocomplete options list with column headers possible?

I would like to know if it is possible to customise the above example so that the list would have column headers such as Title and duration. I have tried to see if I could get it to work using a custom ListBox, but no such luck. Below is a snippet of my own code:
const PopperMy = function (props: PopperProps) {
return <Popper {...props} style={{ width: 500 }} placement='bottom-start' />;
};
return (
<Autocomplete
filterOptions={(x) => x}
getOptionLabel={(option: Record<string, unknown>) => `${option.order}, ${option.name}, ${option.email}, ${option.phone}, ${option.location}`}
renderOption={(props, option: any) => {
return (
<li {...props} key={option.ID} >
Order: {option.order}, Name: {option.name}, Email: {option.email}, Phone: {option.phone}, Location: {option.location}, Status: {option.status}
</li>
);
}}
options={results}
value={selectedValue}
clearOnBlur={false}
freeSolo
PopperComponent={PopperMy}
disableClearable={true}
includeInputInList
onChange={(ev, newValue) => {
setSelectedValue(newValue);
}}
onInputChange={(ev, newInputValue) => {
setInputValue(newInputValue);
}}
renderInput={(params) => (
<TextField {...params} />
)} /> )
this is achievable by customizing the popper component. In your case, something like `
const PopperMy = function (props) {
const { children, ...rest } = props;
return (
<Popper {...rest} placement="bottom-start">
<Box display="flex" justifyContent="space-between" px="16px">
<Typography variant="h6">Title</Typography>
<Typography variant="h6">Year</Typography>
........... rest of the titles
</Box>
{props.children}
</Popper>
);
};
`
would work. Here is a working example i have created - https://codesandbox.io/s/heuristic-golick-4sv24u?file=/src/App.js:252-614

React - Close MUI drawer from nested menu

I'm using this excellent example (Nested sidebar menu with material ui and Reactjs) to build a dynamic nested menu for my application. On top of that I'm trying to go one step further and put it into a Material UI appbar/temporary drawer. What I'd like to achieve is closing the drawer when the user clicks on one of the lowest level item (SingleLevel) however I'm having a tough time passing the toggleDrawer function down to the menu. When I handle the click at SingleLevel I consistently get a 'toggle is not a function' error.
I'm relatively new to this so I'm sure it's something easy and obvious. Many thanks for any answers/comments.
EDIT: Here's a sandbox link
https://codesandbox.io/s/temporarydrawer-material-demo-forked-v11ur
Code is as follows:
Appbar.js
export default function AppBar(props) {
const [drawerstate, setDrawerstate] = React.useState(false);
const toggleDrawer = (state, isopen) => (event) => {
if (event.type === 'keydown' && (event.key === 'Tab' || event.key === 'Shift')) {
return;
}
setDrawerstate({ ...state, left: isopen });
};
return (
<Box sx={{ flexGrow: 1 }}>
<AppBar position="static" color="secondary">
<Toolbar>
<IconButton
size="large"
edge="start"
color="primary"
aria-label="menu"
onClick={toggleDrawer('left', true)}
>
<MenuIcon />
</IconButton>
<img src={logo} alt="logo" />
</Toolbar>
<Drawer
anchor='left'
open={drawerstate['left']}
onClose={toggleDrawer('left', false)}
>
<Box>
<AppMenu toggleDrawer={toggleDrawer} />
</Box>
</Drawer>
</AppBar>
</Box >
)
}
Menu.js
export default function AppMenu(props) {
return MenuItemsJSON.map((item, key) => <MenuItem key={key} item={item} toggleDrawer={props.toggleDrawer} />);
}
const MenuItem = ({ item, toggleDrawer }) => {
const MenuComponent = hasChildren(item) ? MultiLevel : SingleLevel;
return <MenuComponent item={item} toggleDrawer={toggleDrawer} />;
};
const SingleLevel = ({ item, toggleDrawer }) => {
const [toggle, setToggle] = React.useState(toggleDrawer);
return (
<ListItem button onClick={() => { toggle('left', false) }}>
<ListItemIcon>{item.icon}</ListItemIcon>
<ListItemText primary={item.title} />
</ListItem>
);
};
const MultiLevel = ({ item }) => {
const { items: children } = item;
const [open, setOpen] = useState(false);
const handleClick = () => {
setOpen((prev) => !prev);
};
return (
<React.Fragment>
<ListItem button onClick={handleClick}>
<ListItemIcon>{item.icon}</ListItemIcon>
<ListItemText primary={item.title} secondary={item.description} />
{open ? <ExpandLess /> : <ExpandMore />}
</ListItem>
<Collapse in={open} timeout="auto" unmountOnExit>
<List component="div" disablePadding>
{children.map((child, key) => (
<MenuItem key={key} item={child} />
))}
</List>
</Collapse>
</React.Fragment>
);
};
You shouldn't call a react hook inside of any function that is not a react component. Please see React Rules of Hooks
What you could do instead is pass setToggle directly into the Drawer component as a prop and do something like this for it's onClick attribute:
onClick={() => setToggle(<value>)}

React js useState throws error when state is passed as prop

I am writing an ecommerce webshop using React js and Commerce.js
I am very confused as I am not able to identify the precise problem. But here's how it's happening:
My App.js:
const App = () => {
const [products, setProducts] = useState([]);
const [cart, setCart] = useState([]);
const fetchProducts = async () => {
const { data } = await commerce.products.list();
setProducts(data);
};
const fetchCart = async () => {
setCart(await commerce.cart.retrieve());
};
const handleAddToCart = async (productId, quantity) => {
const item = await commerce.cart.add(productId, quantity);
setCart(item.cart);
};
useEffect(() => {
fetchProducts();
fetchCart();
}, []);
console.log(cart);
return (
<>
<Navbar totalItems={cart.total_items} />
{/* <Products products={products} onAddToCart={handleAddToCart} /> */}
{/* <Cart cartItems={cart} /> */}
</>
);
Now when I uncomment the <Cart cartItems={cart} />, React js Throws an error
This is the error in from the console
Uncaught Error: Objects are not valid as a React child (found: object with keys {raw, formatted, formatted_with_symbol, formatted_with_code}). If you meant to render a collection of children, use an array instead.
Interestingly enough, the single item is being passed on through the Cart.js but not without the error.
Here's Cart.js for Reference
const Cart = ({ cartItems }) => {
const classes = useStyles();
const EmptyCart = () => {
return (
<Typography variant="subtitle1">
You have no items in your cart. Start adding some :)
</Typography>
);
};
const FilledCart = () => {
return (
<>
<Grid container spacing={3}>
{cartItems.line_items.map((item) => (
<Grid item xs={12} sm={4} key={item.id}>
<CartItem items={item} />
</Grid>
))}
</Grid>
<div className={classes.cardDetails}>
<Typography variant="h4">
Subtotal: {cartItems.subtotal.formatted_with_symbol}
</Typography>
<div>
<Button
className={classes.emptyButton}
size="large"
type="button"
variant="contained"
color="secondary"
>
Empty Cart
</Button>
<Button
className={classes.checkoutButton}
size="large"
type="button"
variant="contained"
color="primary"
>
Checkout
</Button>
</div>
</div>
</>
);
};
if (!cartItems.line_items)
return <Typography variant="h4">Loading...</Typography>;
return (
<Container>
<div className={classes.toolbar} />
<Typography className={classes.title} variant="h3">
Your Shopping Cart
</Typography>
{!cartItems.line_items.length ? <EmptyCart /> : <FilledCart />}
</Container>
);
};
Update:
Here's what Cart object looks like

Sharing component states with useState() for active link in Nav

I'm trying to learn react and fairly new to the framework. I am trying to create a simple navbar component wih material-ui that is responsive (will show all links on medium devices and up, and open a side drawer on small devices). I have most of it setup to my liking, however, the issue I am currently having, is getting and setting the active link according to the page I am on.
It seems to works correctly on the medium devices and up, but when transitioning to a smaller device, the link is not updated correctly, as it will keep the active link from the medium screen set, while updating the side drawer active link.
Navbar.js
const Navbar = () => {
const classes = useStyles();
const pathname = window.location.pathname;
const path = pathname === '' ? '' : pathname.substr(1);
const [selectedItem, setSelectedItem] = useState(path);
const handleItemClick = (event, selected) => {
setSelectedItem(selected);
console.log(selectedItem);
};
return (
<>
<HideNavOnScroll>
<AppBar position="fixed">
<Toolbar component="nav" className={classes.navbar}>
<Container maxWidth="lg" className={classes.navbarDisplayFlex}>
<List>
<ListItem
button
component={RouterLink}
to="/"
selected={selectedItem === ''}
onClick={event => handleItemClick(event, '')}
>
<ListItemText className={classes.item} primary="Home" />
</ListItem>
</List>
<Hidden smDown>
<List
component="nav"
aria-labelledby="main navigation"
className={classes.navListDisplayFlex}
>
<ListItem
button
component={RouterLink}
to="/account/login"
selected={selectedItem === 'account/login'}
onClick={event => handleItemClick(event, 'account/login')}
>
<ListItemText className={classes.item} primary="Login" />
</ListItem>
<ListItem
button
component={RouterLink}
to="/account/register"
selected={selectedItem === 'account/register'}
onClick={event => handleItemClick(event, 'account/register')}
>
<ListItemText className={classes.item} primary="Register" />
</ListItem>
</List>
</Hidden>
<Hidden mdUp>
<SideDrawer />
</Hidden>
</Container>
</Toolbar>
</AppBar>
</HideNavOnScroll>
<Toolbar id="scroll-to-top-anchor" />
<ScrollToTop>
<Fab aria-label="Scroll back to top">
<NavigationIcon />
</Fab>
</ScrollToTop>
</>
)
}
SideDrawer.js
const SideDrawer = () => {
const classes = useStyles();
const [state, setState] = useState({ right: false });
const pathname = window.location.pathname;
const path = pathname === "" ? "" : pathname.substr(1);
const [selectedItem, setSelectedItem] = useState(path);
const handleItemClick = (event, selected) => {
setSelectedItem(selected);
console.log(selectedItem);
};
const toggleDrawer = (anchor, open) => (event) => {
if (
event &&
event.type === "keydown" &&
(event.key === "Tab" || event.key === "Shift")
) {
return;
}
setState({ ...state, [anchor]: open });
};
const drawerList = (anchor) => (
<div
className={classes.list}
role="presentation"
onClick={toggleDrawer(anchor, false)}
onKeyDown={toggleDrawer(anchor, false)}
>
<List component="nav">
<ListItem
button
component={RouterLink}
to="/account/login"
selected={selectedItem === "account/login"}
onClick={(event) => handleItemClick(event, "account/login")}
>
<ListItemText className={classes.item} primary="Login" />
</ListItem>
<ListItem
button
component={RouterLink}
to="/account/login"
selected={selectedItem === "account/register"}
onClick={(event) => handleItemClick(event, "account/register")}
>
<ListItemText className={classes.item} primary="Register" />
</ListItem>
</List>
</div>
);
return (
<React.Fragment>
<IconButton
edge="start"
aria-label="Menu"
onClick={toggleDrawer("right", true)}
>
<Menu fontSize="large" style={{ color: "white" }} />
</IconButton>
<Drawer
anchor="right"
open={state.right}
onClose={toggleDrawer("right", false)}
>
{drawerList("right")}
</Drawer>
</React.Fragment>
);
};
Code Sandbox - https://codesandbox.io/s/async-water-yx90j
I came across this question on SO: Is it possible to share states between components using the useState() hook in React?, which suggests that I need to lift the state up to a common ancestor component, but I don't quite understand how to apply this in my situation.
I would suggest to put aside for a moment your code and do a playground for this lifting state comprehension. Lifting state is the basic strategy to share state between unrelated components. Basically at some common ancestor is where the state and setState will live. there you can pass down as props to its children:
const Parent = () => {
const [name, setName] = useState('joe')
return (
<>
<h1>Parent Component</h1>
<p>Child Name is {name}</p>
<FirstChild name={name} setName={setName} />
<SecondChild name={name} setName={setName} />
</>
)
}
const FirstChild = ({name, setName}) => {
return (
<>
<h2>First Child Component</h2>
<p>Are you sure child is {name}?</p>
<button onClick={() => setName('Mary')}>My Name is Mary</button>
</>
)
}
const SecondChild = ({name, setName}) => {
return (
<>
<h2>Second Child Component</h2>
<p>Are you sure child is {name}?</p>
<button onClick={() => setName('Joe')}>My Name is Joe</button>
</>
)
}
As you can see, there is one state only, one source of truth. State is located at Parent and it passes down to its children. Now, sometimes it can be troublesome if you need your state to be located at some far GreatGrandParent. You would have to pass down each child until get there, which is annoying. if you found yourself in this situation you can use React Context API. And, for most complicated state management, there are solutions like redux.

How to store checkbox values using localStorage? (react+hooks)

Here is my Todolist component, which contains a List, and all list items with checkboxes and with material list and checkboxes. Two props are passed: todos and deleteTodo.
const TodoList = ({ todos, deleteTodo}) => {
return (
<List>
{todos.map((todo, index) => (
<ListItem key={index.toString()} dense button>
<Checkbox disableRipple/>
<ListItemText key={index} primary={todo} />
<ListItemSecondaryAction>
<IconButton
aria-label="Delete"
onClick={() => {
deleteTodo(index);
}}
>
<DeleteIcon />
</IconButton>
</ListItemSecondaryAction>
</ListItem>
))}
</List>
);
};
I figured out how to use local storage for storing the todos as an array, but have no idea how to store the checkbox values. Can somebody explain, what would be the strategy for that?
And here is the main app:
const initialValue = () => {
const initialArray = localStorage.getItem("todos");
return JSON.parse(initialArray);
};
const [todos, setTodos] = useState(initialValue);
useEffect(() => {
const json = JSON.stringify(todos);
localStorage.setItem("todos", json);
});
return (
<div className="App">
<Typography component="h1" variant="h2">
Todos
</Typography>
<TodoForm
saveTodo={todoText => {
const trimmedText = todoText.trim();
if (trimmedText.length > 0) {
setTodos([...todos, trimmedText]);
}
}}
/>
<TodoList
todos={todos}
deleteTodo={todoIndex => {
const newTodos = todos.filter((_, index) => index !== todoIndex);
setTodos(newTodos);
}}
/>
</div>
);
};
I would appreciate any suggestions or directions, how to tackle this problem. Thx
One approach would be to use the onChange callback of the Checkbox component
e.g. <Checkbox disableRipple onChange={(e)=> onCheckboxChange(e.event.target) /> (and whatever params you need)
and pass it up to your parent component through a prop, e.g.
const TodoList = ({ todos, deleteTodo, onCheckboxChange}) => {
You can then store the value in local storage the parent component.
There may be a more elegant approach

Categories

Resources