Downshift: Set inputValue to value of currently highlighted item on keyboard navigation - javascript

Using Downshift, how would one implement settting the inputValue to the value of the currently highlighted item on ArrowUp/ArrowDown while persisting the filtered items until the user manually augments the inputValue?
e.g:

The aforementioned behaviour can be implemented leveraging the stateReducer and useCombobox hook and as follows:
import React, { useState } from "react";
import { render } from "react-dom";
import { useCombobox } from "downshift";
import { items, menuStyles } from "./utils";
function stateReducer(state, actionAndChanges) {
switch (actionAndChanges.type) {
case useCombobox.stateChangeTypes.InputChange:
return {
...actionAndChanges.changes,
userInput: actionAndChanges.changes.inputValue
};
case useCombobox.stateChangeTypes.InputKeyDownArrowDown:
case useCombobox.stateChangeTypes.InputKeyDownArrowUp:
if (!actionAndChanges.changes.inputValue) return actionAndChanges.changes;
return {
...actionAndChanges.changes,
userInput: actionAndChanges.changes.inputValue,
inputValue: actionAndChanges.getItemNodeFromIndex(
actionAndChanges.changes.highlightedIndex
).innerText
};
default:
return actionAndChanges.changes; // otherwise business as usual.
}
}
function DropdownSelect() {
const [inputItems, setInputItems] = useState(items);
const {
isOpen,
getToggleButtonProps,
getLabelProps,
getMenuProps,
getInputProps,
getComboboxProps,
highlightedIndex,
getItemProps
} = useCombobox({
items: inputItems,
stateReducer,
onInputValueChange: ({ userInput, inputValue }) => {
if (userInput === inputValue) {
const filteredItems = items.filter(item =>
item.toLowerCase().startsWith(inputValue.toLowerCase())
);
setInputItems(filteredItems);
}
}
});
return (
<React.Fragment>
<label {...getLabelProps()}>Choose an element:</label>
<div style={{ display: "inline-block" }} {...getComboboxProps()}>
<input {...getInputProps()} />
<button {...getToggleButtonProps()} aria-label="toggle menu">
↓
</button>
</div>
<ul {...getMenuProps()} style={menuStyles}>
{isOpen &&
inputItems.map((item, index) => (
<li
style={
highlightedIndex === index ? { backgroundColor: "#bde4ff" } : {}
}
key={`${item}${index}`}
{...getItemProps({ item, index })}
>
{item}
</li>
))}
</ul>
</React.Fragment>
);
}
render(<DropdownSelect />, document.getElementById("root"));
View the Code sandbox here

Related

How to make an onClick event on a button work the same way as the onClick event defined for other Button in react

I just started learning react and came across this problem where I has two buttons(Add button in menu-item and add button in cart) each defined in different components. I defined the functionality and added logic through props for Add button in menu-item which when user clicks updates the quantity , cartCount and adds item details and quantity to the cart. Now, when I click on Add button in cart, I want a similar functionalty like it should update quantity in cart, quantity in menu-item and cart-count. I know there should be some way without repeating the entire logic again. I know this is a long one. Thanks in Advance for Answering!!!
App.js
import react, { useState, useEffect } from "react";
import Header from "./components/Header";
import LandingPage from "./components/LandingPage";
import MenuItems from "./components/menuItems";
import Cart from "./components/Cart";
import ItemContext from "./store/item-context";
function App() {
const [items, setItems] = useState([]);
const [total, setTotal] = useState(0);
useEffect(() => {
setTotal(() => {
return items.reduce((acc, eachItem) => {
return eachItem.quantity + acc;
}, 0)
})
}, [items])
const [cartBool, setCartBool] = useState(false);
function AddedItem(item) {
const foundIndex = items.findIndex(eachItem => {
return eachItem.title === item.title;
})
if (foundIndex !== -1) {
setItems(prev => {
prev[foundIndex].quantity = item.quantity;
return [...prev];
})
}
else {
setItems(prev => {
return [...prev, item]
})
}
}
function handleCartClick() {
setCartBool(true);
}
function handleCloseClick() {
setCartBool(false);
}
return (
<react.Fragment>
<ItemContext.Provider value={{
items: items
}}>
{cartBool &&
<Cart onCloseClick={handleCloseClick} />}
<div className="parent-container">
<Header cartCount={total} onCartClick={handleCartClick} />
<LandingPage />
<MenuItems onAddItem={AddedItem} />
</div>
</ItemContext.Provider>
</react.Fragment>
);
}
export default App;
Menu-items.js
import react from "react";
import MenuItem from "./menuItem";
import MenuContent from "./menuContent";
function MenuItems(props) {
function handleItems(item){
props.onAddItem(item);
}
return (
<div className="menu">
{MenuContent.map(eachItem =>{
return <MenuItem title={eachItem.title} description={eachItem.description} price={eachItem.price} key={eachItem.key} onAdd={handleItems}/>
})}
</div>
);
}
export default MenuItems;
Menu-item.js
import react , { useState } from "react";
function MenuItem(props) {
const [item, setItem] = useState({
title: "",
quantity: 0,
price: ""
});
function handleClick(){
setItem(prev =>{
return {
title: props.title,
quantity: prev.quantity + 1,
price: props.price
}
})
}
function handleSubmit(event){
event.preventDefault();
props.onAdd(item);
}
return (
<div className="menu-item">
<div className="menu-content">
<h3>{props.title}</h3>
<p>{props.description}</p>
<h4>{props.price}</h4>
</div>
<form onSubmit={handleSubmit} className="add-items">
<label htmlFor="Amount">Amount</label>
<input onChange={() => {}} type="number" name="Amount" value={item.quantity}/>
<button onClick={handleClick} type="submit" className="btn btn-lg">Add</button>
</form>
</div>
);
}
export default MenuItem;`
Cart.js
import react, { useContext } from "react";
import CartItem from "./cartItem";
import ItemContext from "../store/item-context";
function Cart(props) {
const ctx = useContext(ItemContext);
function handleCloseClick(){
props.onCloseClick();
}
return (
<div className="cart-modal">
<div className="card">
{ctx.items.map((eachItem, index) =>{
return <CartItem title={eachItem.title} price={eachItem.price} quantity={eachItem.quantity} key={index} onAdd={props.onAddItem} onRemove={props.RemoveItem}/>
})}
<footer>
<button className="btn btn-lg" onClick={handleCloseClick}>Close</button>
<button className="btn btn-lg">Order</button>
</footer>
</div>
</div>
);
}export default Cart;
cartItem.js
import react, { useState } from "react";
function CartItem(props) {
const [item, setItem] = useState({
title: props.title,
price: props.price,
quantity: props.quantity
})
function handlePlusClick(){
setItem(prev =>{
prev.quantity = prev.quantity + 1
return prev
})
props.onAdd(item);
}
function handleMinusClick(){
var updatedQuantity;
setItem(prev =>{
prev.quantity = prev.quantity -1
updatedQuantity = prev.quantity
return prev;
})
if(updatedQuantity > 0){
props.onAdd(item);
}
else{
props.onRemove(item);
}
}
return (
<div className="cart-item">
<div className="cart-content">
<h1>{props.title}</h1>
<p>{props.price}
<span> X {props.quantity}</span>
</p>
</div>
<div className="button-controls">
<button onClick={handleMinusClick}>-</button>
<button onClick={handlePlusClick}>+</button>
</div>
</div>
);
}export default CartItem;
I tried creating new item object when user clicked on + button in cartItem and sent it to AddedItem function in App.js and it is working(even though it is not the best practice out there)!!! But it is also updating the item.quantity in menuItem component too. it is working as expected... But I have no idea why it is going back and updating the menuItem quantity as well. is it because of useContext and I wrapped it around all the components I'm rendering??
Updates in Response to OP 2/18
Your example is still a bit hard to follow and reproduce since we can't see MenuContent and the use of useContext is confusing.
But it sounds like both your menu and the cart are using the same items state or at least something along those lines is happening.
Your code demonstrates a handle on state management but I think you need to take a step back and think about what parts of your app should be stateful and what strategies are needed. You don't need useContext but I suppose it's an opportunity to illustrate the differences and advantages.
State Management Overview
For now I'll assume your menu items are a list of items that aren't really changing. You cart will need some state since you need to track the items along with their quantity and use this information to calculate cart totals.
Where do we need to update or access our cart state?
MenuItem - Our menu item has an Add button that should update the cart state with the new quantity. We don't need the cart items here, but we do need to handle the logic to update our cart.
Cart - Our cart needs to access the cart state to a) show the cart items and b) to increment or decrement the quantity of specific items (+ and -).
You can do this with prop drilling using the same strategies used in your code so far (that you've shared) OR you can use useContext.
To demonstrate the difference, below is a more complete solution with useContext. All state management logic for the cart is bundled into our cart context and our provider lets parts of our app access this without relying so much on props.
Example/Demo Full Solution with useContext (Click to View)
https://codesandbox.io/s/update-cart-example-use-context-4glul7
import "./styles.css";
import React, { useState, createContext, useContext, useReducer } from "react";
const CartContext = createContext();
const initialCartState = { cartItems: [], totalCost: 0, totalQuantity: 0 };
const actions = {
INCREMENT_ITEM: "INCREMENT_ITEM",
DECREMENT_ITEM: "DECREMENT_ITEM",
UPDATE_QUANTITY: "UPDATE_QUANTITY"
};
const reducer = (state, action) => {
const existingCartItem = state.cartItems.findIndex((item) => {
return item.id === action.itemToUpdate.id;
});
switch (action.type) {
case actions.INCREMENT_ITEM:
return {
cartItems: state.cartItems.map((item) =>
item.id === action.itemToUpdate.id
? {
...item,
quantity: item.quantity + 1
}
: item
),
totalQuantity: state.totalQuantity + 1,
totalCost: state.totalCost + action.itemToUpdate.price
};
case actions.DECREMENT_ITEM:
return {
cartItems: state.cartItems.map((item) =>
item.id === action.itemToUpdate.id
? {
...item,
quantity: item.quantity - 1
}
: item
),
totalQuantity: state.totalQuantity - 1,
totalCost: state.totalCost - action.itemToUpdate.price
};
case actions.UPDATE_QUANTITY:
return {
cartItems:
existingCartItem !== -1
? state.cartItems.map((item) =>
item.id === action.itemToUpdate.id
? {
...item,
quantity: item.quantity + action.itemToUpdate.quantity
}
: item
)
: [...state.cartItems, action.itemToUpdate],
totalQuantity: state.totalQuantity + action.itemToUpdate.quantity,
totalCost:
state.totalCost +
action.itemToUpdate.quantity * action.itemToUpdate.price
};
default:
return state;
}
};
const CartProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initialCartState);
const value = {
cartItems: state.cartItems,
totalQuantity: state.totalQuantity,
totalCost: state.totalCost,
incrementItem: (itemToUpdate) => {
dispatch({ type: actions.INCREMENT_ITEM, itemToUpdate });
},
decrementItem: (itemToUpdate) => {
dispatch({ type: actions.DECREMENT_ITEM, itemToUpdate });
},
updateQuantity: (itemToUpdate) => {
dispatch({ type: actions.UPDATE_QUANTITY, itemToUpdate });
}
};
return <CartContext.Provider value={value}>{children}</CartContext.Provider>;
};
export default function App() {
return (
<CartProvider>
<MenuItems />
<Cart />
</CartProvider>
);
}
const menuItems = [
{ title: "item 1", description: "description 1", price: 10, id: "1" },
{ title: "item 2", description: "description 2", price: 20, id: "2" },
{ title: "item 3", description: "description 3", price: 30, id: "3" }
];
function MenuItems(props) {
return (
<div className="menu">
{menuItems.map((item) => {
return (
<MenuItem
title={item.title}
description={item.description}
price={item.price}
key={item.id}
// added this as prop
id={item.id}
/>
);
})}
</div>
);
}
function MenuItem(props) {
const { updateQuantity } = useContext(CartContext);
const [item, setItem] = useState({
title: props.title,
quantity: 0,
price: props.price,
// included a unique item id here
id: props.id
});
// Don't need this anymore...
// function handleClick(e) {
// ...
// }
// update quantity as we type by getting as state...
function changeQuantity(e) {
e.preventDefault();
setItem((prev) => {
return {
...prev,
quantity: Number(e.target.value)
};
});
}
function handleSubmit(e, item) {
e.preventDefault();
updateQuantity(item);
}
return (
<div className="menu-item">
<div className="menu-content">
<h3>{props.title}</h3>
<p>{props.description}</p>
<h4>Price: ${props.price}</h4>
</div>
<form onSubmit={(e) => handleSubmit(e, item)} className="add-items">
<label htmlFor="Amount">Amount</label>
<input
onChange={changeQuantity}
type="number"
name="Amount"
value={item.quantity}
/>
{/* No need for onClick on button, onSubmit already handles it */}
<button type="submit" className="btn btn-lg">
Add
</button>
</form>
</div>
);
}
function Cart() {
const {
cartItems,
totalQuantity,
totalCost,
incrementItem,
decrementItem
} = useContext(CartContext);
return (
<div>
<h2>Cart</h2>
<h3>Items:</h3>
{cartItems.length > 0 &&
cartItems.map(
(item) =>
item.quantity > 0 && (
<div key={item.id}>
{item.title}
<br />
<button onClick={() => decrementItem(item)}> - </button>{" "}
{item.quantity}{" "}
<button onClick={() => incrementItem(item)}> + </button>
</div>
)
)}
<h3>Total Items: {totalQuantity}</h3>
<h3>Total Cost: {`$${Number(totalCost).toFixed(2)}`}</h3>
</div>
);
}
Original Response
It sounds like you wanted the cart to update whenever Add was clicked in MenuItem.
Fixing use of onClick and onSubmit
This was part of your issue. In MenuItem you used a form and had onClick on your form submit button. Since your button has type="submit" it will fire submit event along with onSubmit handler. We can simply use onSubmit as our handler here and remove the onClick from the button.
I simplified MenuItem to update and read quantity value from state. Then when adding the item we simply pass the item (since it already has the up-to-date quantity).
Your logic was basically there. I gave each product an id to simplify keeping track with all the prop drilling versus using title or key as it was just a bit easier for me to wrap my head around. Hopefully the changes and comments make sense.
Example/Demo (Click to view)
https://codesandbox.io/s/update-cart-example-veic1h
import "./styles.css";
import React, { useState, createContext, useContext, useEffect } from "react";
const CartContext = createContext();
export default function App() {
const [cartItems, setCartItems] = useState([]);
const [totalQuantity, setTotalQuantity] = useState(0);
const [totalCost, setTotalCost] = useState(0);
useEffect(() => {
setTotalQuantity(() => {
return cartItems.reduce((acc, item) => {
return item.quantity + acc;
}, 0);
});
setTotalCost(() => {
return cartItems.reduce((acc, item) => {
return item.quantity * item.price + acc;
}, 0);
});
}, [cartItems]);
function addItemToCart(newItem) {
const existingCartItem = cartItems.findIndex((item) => {
return item.id === newItem.id;
});
setCartItems((prevItems) => {
return existingCartItem !== -1
? prevItems.map((prevItem) =>
prevItem.id === newItem.id
? {
...prevItem,
quantity: prevItem.quantity + newItem.quantity
}
: prevItem
)
: [...prevItems, newItem];
});
// the above is similar to what you have below,
// but good practice not to mutate state directly
// in case of incrementing item already found in cart...
// if (foundIndex !== -1) {
// setCartItems((prev) => {
// prev[foundIndex].quantity = item.quantity;
// return [...prev];
// });
// } else {
// setCartItems((prev) => {
// return [...prev, item];
// });
// }
}
return (
<CartContext.Provider value={{ cartItems, totalQuantity, totalCost }}>
<div className="parent-container">
<MenuItems onAddItem={addItemToCart} />
<Cart />
</div>
</CartContext.Provider>
);
}
const menuItems = [
{ title: "item 1", description: "description 1", price: 10, id: "1" },
{ title: "item 2", description: "description 2", price: 20, id: "2" },
{ title: "item 3", description: "description 3", price: 30, id: "3" }
];
function MenuItems(props) {
function handleItems(item) {
props.onAddItem(item);
}
return (
<div className="menu">
{menuItems.map((item) => {
return (
<MenuItem
title={item.title}
description={item.description}
price={item.price}
key={item.id}
// added this as prop
id={item.id}
onAdd={handleItems}
/>
);
})}
</div>
);
}
function MenuItem(props) {
const [item, setItem] = useState({
title: props.title,
quantity: 0,
price: props.price,
// included a unique item id here
id: props.id
});
// Don't need this anymore...
// function handleClick(e) {
// ...
// }
// update quantity as we type by getting as state...
function changeQuantity(e) {
e.preventDefault();
setItem((prev) => {
return {
...prev,
quantity: Number(e.target.value)
};
});
}
function handleSubmit(event) {
event.preventDefault();
props.onAdd(item);
}
return (
<div className="menu-item">
<div className="menu-content">
<h3>{props.title}</h3>
<p>{props.description}</p>
<h4>Price: ${props.price}</h4>
</div>
<form onSubmit={handleSubmit} className="add-items">
<label htmlFor="Amount">Amount</label>
<input
onChange={changeQuantity}
type="number"
name="Amount"
value={item.quantity}
/>
{/* No need for onClick on button, onSubmit already handles it */}
<button type="submit" className="btn btn-lg">
Add
</button>
</form>
</div>
);
}
function Cart() {
const cart = useContext(CartContext);
const { cartItems, totalQuantity, totalCost } = cart;
return (
<div>
<h2>Cart</h2>
<h3>Items:</h3>
{cartItems.length > 0 &&
cartItems.map(
(item) =>
item.quantity > 0 && (
<div key={item.id}>
{item.title} - quantity: {item.quantity}
</div>
)
)}
<h3>Total Items: {totalQuantity}</h3>
<h3>Total Cost: {`$${Number(totalCost).toFixed(2)}`}</h3>
</div>
);
}

Why isn't the "active-work" class getting added with the onClick event?

I'm trying to add an active class to some buttons in react but they're not applying. I'm not getting any errors or warnings and the syntax seems correct. What am i missing?
I have some handleEvent functions in JSX, I was expecting the "active-work" class to be applied to the span within the "work__filters" div. Everything else seems to be working. Code:
export default Works
part of CSS not getting applied:
import React, { useEffect, useState } from 'react'
import { projectsData } from './Data'
import { projectsNav } from './Data'
import WorkItems from './WorkItems';
import "./work.css";
const Works = () => {
const [item, setItem] = useState({ name: 'all'});
const [projects, setProjects] = useState([]);
const [active, setActive] = useState(0);
useEffect(() => {
if(item.name === "all") {
setProjects(projectsData);
}
else {
const newProjects = projectsData.filter((project) => {
return project.category.toLowerCase() === item.name;
});
setProjects(newProjects);
}
}, [item]);
const handleClick = (e, index) => {
setItem({ name: e.target.textContent.toLowerCase()});
setActive(index);
};
return (
<div>
<div className="work__filters">
{projectsNav.map((item, index) => {
return (
<span
onClick={(e) => {
handleClick(e, index);
}}
className={`${active === index ? 'active-work' : ""}work__item`}
key={index}>
{item.name}
</span>
);
})}
</div>
<div className="work__container container grid">
{projects.map((item) => {
return <WorkItems item={item} key={item.id}/>
})}
</div>
</div>
);
}
export default Works
//CSS not being applied
.active-work {
background-color: black;
color: white;
}
The code looks fine at first glance, except you need to put a space between work__item and the condition. You should see with the Devtools that the class is getting applied, but it will probably look like class="active-workwork__item", hence the styling is not applied.
https://codesandbox.io/embed/sleepy-bird-975b1m?fontsize=14&hidenavigation=1&theme=dark
<span
onClick={(e) => {
handleClick(e, index);
}}
className={`${active === index ? "active-work" : ""} work__item`}
key={index}
>

React context "update" not working properly

I'm making custom tab component and I have issue on closing tab. Switching tab is working ok but when I close last tab I want to set active previouse tab and this is not working.
setTabs (context update) is updating array "data" in object but not "activeTab".
I'm using react Context to store list of tabs and active tab.
Switching tabs is working correctly, opening new tab also works fine.
TabsContext
import React, { useReducer, useEffect } from "react";
const initialState = {
data: [
{
name: "Start",
component: "StartTab",
cantClose: true,
params: null
}
],
activeTab: 0
};
const localState = JSON.parse(localStorage.getItem("tabs"));
const TabsContext = React.createContext();
let reducer = (tabs, newTabs) => {
if (newTabs === null) {
localStorage.removeItem("tabs");
return initialState;
}
return { ...tabs, ...newTabs };
};
function TabsProvider(props) {
const [tabs, setTabs] = useReducer(reducer, localState || initialState);
useEffect(() => {
localStorage.setItem("tabs", JSON.stringify(tabs));
}, [tabs]);
return (
<TabsContext.Provider value={{ tabs, setTabs }}>
{props.children}
</TabsContext.Provider>
);
}
export { TabsContext, TabsProvider };
MainTabs Component:
import React, { Component, useContext } from "react";
import { TabsContext } from "../../providers/TabsProvider";
import StartTab from "./tab.start";
...
class TabComponent extends Component {
components = {
StartTab: StartTab,
...
};
render() {
const TagName = this.components[this.props.tag];
return <TagName />
}
}
const TabsMain = () => {
const { tabs, setTabs } = useContext(TabsContext);
const closeTab = (index) => {
tabs.data.splice(index, 1);
if (tabs.activeTab == tabs.data.length ) {
tabs.activeTab--;
}
setTabs({ data: tabs.data, activeTab: tabs.activeTab });
};
const tabsNavigation = tabs.data.map((tab, index) =>
<li key={index}>
<button
onClick={() => {
setTabs({ data: tabs.data, activeTab: index });
}}
className={`${tabs.activeTab == index ? 'active' : ''}`}
>
{tab.name}
<div onClick={() => {
closeTab(index);
}} className={`close_button ${!tab.cantClose ? 'show' : 'hide'}`}>X</div>
</button>
</li>
);
const tabsPanels = tabs.data.map((tab, index) =>
<div key={index} className={`panel ${tabs.activeTab == index ? 'active' : ''}`}>
<TabComponent tag={tab.component} />
</div>
);
return (
<div className="tabs">
<ul className="tabs__navigation">
{tabsNavigation}
</ul>
<div className="tabs__content">
{tabsPanels}
</div>
</div>
);
};
export default TabsMain;
Navigation Component
import React, { Component, useContext } from "react";
import { TabsContext } from "../../providers/TabsProvider";
const Navigation = () => {
const { tabs, setTabs } = useContext(TabsContext);
const openTab = (newTab) => {
tabs.data.push(newTab);
setTabs(tabs);
};
return (
<ul className="navigation">
<li>
<button onClick={() => { openTab({ name: "Start", component: "StartTab" }); }}>
Start
</button>
</li>
</ul>
);
};
export default Navigation;
I found the solution.
First of all I have double click action:
1) on click on tab - selectTab,
2) on "X" on same button, when i clicked X then selectTab and closeTab are fired (setTabs was fired also two times)
The solution Was to extract "X" from button - from this:
<button
onClick={() => {
setTabs({ data: tabs.data, activeTab: index });
}}
className={`${tabs.activeTab == index ? 'active' : ''}`}
>
{tab.name}
<div onClick={() => {
closeTab(index);
}} className={`close_button ${!tab.cantClose ? 'show' : 'hide'}`}>X</div>
</button>
to this:
<button
onClick={() => {
setTabs({ data: tabs.data, activeTab: index });
}}
className={`${tabs.activeTab == index ? 'active' : ''}`}
>
{tab.name}
</button>
<div onClick={() => {
closeTab(index);
}} className={`close_button ${!tab.cantClose ? 'show' : 'hide'}`}>X</div>
and change closeTab function to this:
const closeTab = (index) => {
tabs.data.splice(index, 1);
setTabs({ data: tabs.data, activeTab: index-1 });
};

How i can replace states ( arrays ) ReactJs

today I offer you a new challenge
my problem and the next
in the following code
I map objects in my database
I also map a list of items
and so basically I would like to pass my database and not the Items table
basically I want to do exactly the same thing as in the following code except that instead of using the items array, I would like to be able to use the data array which contains my database
do you have any idea how to do this?
I hope I was precise thanks to you for the help Neff
ps: Sorry for the length of the code i try to do my best to clean a little
import React, { Component } from 'react';
import { CardText, Card,Row, Col, Button } from 'reactstrap';
import axios from 'axios'
import GridLayout from 'react-grid-layout';
import SmsForm from './Sms/SMSForm'
import FruitList from './FruitList'
import './AdminPage.scss'
const UP = -1;
const DOWN = 1;
const entrypoint = process.env.REACT_APP_API_ENTRYPOINT+'/api';
class AdminPage extends Component {
constructor(props) {
super(props);
this.state = {
items: [
{ id: 1, name: "orange", bgColor: "#f9cb9c" },
{ id: 2, name: "lemon", bgColor: "#fee599" },
{ id: 3, name: "strawberry", bgColor: "#e06666" }
],
data: [],
}
handleMove = (id, direction) => {
const { items } = this.state;
const position = items.findIndex(i => i.id === id);
if (position < 0) {
throw new Error("Given item not found.");
} else if (
(direction === UP && position === 0) ||
(direction === DOWN && position === items.length - 1)
) {
return; // canot move outside of array
}
const item = items[position]; // save item for later
const newItems = items.filter(i => i.id !== id); // remove item from array
newItems.splice(position + direction, 0, item);
this.setState({ items: newItems });
};
// rest of the component
onHandleChange(event) {
const name = event.target.getAttribute('name');
this.setState({
message: { ...this.state.message, [name]: event.target.value }
});
}
getRandom = async () => {
const res = await axios.get(
entrypoint + "/alluserpls"
)
this.setState({ data: res.data })
}
componentDidMount() {
this.getRandom()
}
render() {
let datas = this.state.data.map(datass => {
const status = JSON.parse(localStorage.getItem("validated-order") || "{}")[datass.id];
return (
<div>
< Col sm="12" key={datass.id} className="wtfFuHereIsForOnlyBackGroundColorForCol12Nice">
<FruitList fruitList={this.state.items} onMove={this.handleMove} />
<GridLayout className="GridlayoutTextOnlyForGridOuiAndHeigthbecauseHeigthWasBug" layout={layout} cols={12} rowHeight={30} width={1200}>
<div key="a">
<Card body className="yoloCardBodyForbackGroundAndRaduisBorderForAdminPageWtf">
<CardText className="cardTextForAdminPageForDataName"> Commande de {datass.name}</CardText>
</Card>
</div>
</ Col>
</div>
)
})
return (
<div> <div>
<Row className="wtfHereIsAgainOnlyForRowAndMarginForAdminPageJustWtf">
<div className="isJustForOnlyPaddingOnRowForAdminPage" >
<div className="navBarGridMenuAdminPage">
<div className="thatIsOnlyForSpaceArroundOnDivAdminPage">
<CardText className="maybeColForAdminPageOuiOui"> Nouvelles commandes </CardText>
</div>
</div>
<div>
{datas}
</div>
</div>
</Row>
</div>
<div className="box">
</div>
</div>
)
}
}
export default AdminPage
here my second components
import React from "react";
const LEFT = -1;
const RIGHT = 1;
class FruitList extends React.Component {
render() {
const { fruitList, onMove } = this.props;
return (
<div style={{ display: "flex" }}>
{fruitList.map(item => (
<div
key={item.id}
style={{
backgroundColor: item.bgColor,
display: "flex"
}}
>
<div className="fruitsArrows">
<a onClick={() => onMove(item.id, LEFT)}>←</a>
</div>
<div className="fruitsId">{item.id}</div>
<div className="fruitsName">{item.name}</div>
<div className="fruitsArrows">
<a onClick={() => onMove(item.id, RIGHT)}>→</a>
</div>
</div>
))}
</div>
);
}
}
export default FruitList;
To delete particular list do like this-
pass your item(object).
<a onClick={() => onMove(item)}>→</a>
handleMove function
handleMove = (row) => {
let filtered = this.state.items.filter(item=>item.id!==row.id);
this.setState({ items: filtered});
};

Hide/Show content using ReactJS

I am trying to implement to Show More/Show Less. So far I have was able to bring up a ItemViewer Component where in I display the list of items. For each section there would be Show More / Show Less links. Show More should be visible whenever the number of items is greater than 3 and it should be able to toggle(Show More/ Show Less). When the number of items is less than 3 dont show the link. Also when there is no data display "No data found".
I have come up with a sandbox : https://codesandbox.io/s/pensive-kirch-1fgq3
Can someone help me here?
import React from "react";
import ReactDOM from "react-dom";
import ItemViewer from "./Item";
const item1 = ["i1d1", "i2d2", "i3d3"];
const item2 = ["i2d1", "i2d2", "i2d3", "i2d4"];
const item3 = ["i3d1", "i3d2", "i3d3", "i3d4", "i3d5"];
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
item1: [],
item2: [],
item3: []
};
}
componentDidMount() {
this.setState({
item1,
item2,
item3
});
}
render() {
let { item1, item2, item3 } = this.state;
return (
<>
<ItemViewer index="1" item="item1" itemData={item1} />
<ItemViewer index="2" item="item2" itemData={item2} />
<ItemViewer index="3" item="item3" itemData={item3} />
</>
);
}
}
import React, { useState } from "react";
const ItemViewer = props => {
function renderItems(list, itemType) {
if (list && list.length > 0) {
return (
<>
<ul>
{list.map(function(item) {
return <li key={item}>{item}</li>;
})}
</ul>
</>
);
} else {
return <p>No Items Found</p>;
}
}
return (
<div>
<span>
{props.index}: {props.item}
</span>
<div>
Show more
</div>
<div>
Show Less
</div>
<div>{renderItems(props.itemData, props.item, props.itemDetailData)}</div>
</div>
);
};
export default ItemViewer;
You can keep a toggle state inside ItemViewer component, and using that value you can decide to show more or show less.
Codesandbox
import React, { useState } from "react";
const ItemViewer = ({ index, itemData, item }) => {
const [toggle, setToggle] = useState(false);
function renderItems(list) {
if (list && list.length > 0) {
if (list.length > 3 && toggle === false) {
return renderList(list.slice(0, 3), "Show More");
} else if (list.length > 3 && toggle === true) {
return renderList(list, "Show Less");
} else if (list.length === 3) {
return renderList(list, "", false);
}
} else {
return <p>No Items Found</p>;
}
}
function renderList(list, buttonText, showButton = true) {
return (
<div>
<ul>
{list.map(function(item) {
return <li key={item}>{item}</li>;
})}
</ul>
{showButton && (
<div>
<button onClick={toggleHandler}>{buttonText}</button>
</div>
)}
</div>
);
}
const toggleHandler = () => {
setToggle(prev => !prev);
};
return (
<div>
<span>
{index}: {item}
</span>
<div>{renderItems(itemData)}</div>
</div>
);
};
export default ItemViewer;
use component state and onClick listener:
function renderItems(list, itemType) {
if (list && list.length > 0 && this.state.showMore1) {
...
<div>
{" "}
<a onClick={() => this.setState({ showMore1: !this.state.showMore1 )} href="#">Show {this.state.showMore1 ? 'Less' : 'More'}</a>
</div>
Use a handler for show events.
https://codesandbox.io/s/blissful-swirles-rchv0
handleShowEvents(index) {
let showMore = [...this.state.showMore];
showMore[index] = !showMore[index];
this.setState({
...this.state,
showMore: showMore
});
}
Also, use a custom list builder.
itemBuilder() {
let items = [];
for (let i = 0; i < this.state.showMore.length; i++) {
const item = `item${i + 1}`;
if (this.state.showMore[i]) {
items.push(
<ItemViewer
index={i}
item={item}
itemData={this.state.items[i]}
handleShowEvents={this.handleShowEvents}
/>
);
} else {
items.push(
<ItemViewer
index={i}
item={item}
itemData={this.state.items[i].slice(0, 3)}
handleShowEvents={this.handleShowEvents}
/>
);
}
}
return items;
}
Check out this https://codesandbox.io/s/smoosh-shape-vinp6
Let me know if it works for you.
Happy Coding:)

Categories

Resources