onChange event won't fire in .map function React - reactjs

I cannot get this onChange event to fire? The goal is to render multiple options from a drop down, from some data I have, then console log "hello" when any of the options is clicked...
It doesn't seem to want to let me use onChange or onClick events in my rendered option elements. If i can simply console log first , then i can figure out everything else. I only posted the necessary code but I can post the rest if needed!
const SlideData = [
{
title: "Slide 0",
},
{
title: "Slide 1",
},
{
title: "Slide 2",
},
];
export default SlideData;
let options = SlideData.map((item, index) => (
<option
key={index}
value={index}
onChange={() => {
console.log("hello");
}}
>
{item.title}
</option>
));
<select className={styles.select} onChange={goto}>
<option>--Select--</option>
{options}
</select>

option tag doesn't support onChange. You can use onClick instead.
onClick={() => {
console.log("hello");
}}

goal was to pass the index and console log it technically. so I guess this worked for me
const slideRef = useRef();
const goto = ({ target }) => {
slideRef.current.goTo(parseInt(target.value, 10));
console.log(target.value);
};
const options = SlideData.map((item, index) => (
<option key={index} value={index}>
{item.title}
</option>
));
return (
<div className={styles.container}>
<Slide ref={slideRef} {...properties}>
<div className={styles.slide}>First Slide</div>
<div className={styles.slide}>Second Slide</div>
<div className={styles.slide}>Third Slide</div>
<div className={styles.slide}>Fourth Slide</div>
<div className={styles.slide}>Fifth Slide</div>
</Slide>
<div>
<Button type="button" onClick={back}>
Back
</Button>
<Button type="button" onClick={next}>
Next
</Button>
<select className={styles.select} onChange={goto}>
<option>--Select--</option>
{options}
</select>
</div>
</div>
);

Related

options tag in react cannot print the value from sever but showing value in console window

const AddPost = () => {
const [categories, setCategory] = useState([]);
var catt = categories;
useEffect(() => {
loadAllCategories()
.then((data) => {
// console.log(data);
setCategory(data);
})
.catch((error) => {
console.log(error);
});
}, []);
const [content, setContent] = useState("");
return (
<div className="wrapper">
<Card className="shadow mt-2">
<CardBody>
<Form>
<div className="my-3">
<Label for="category">Post Category</Label>
<Input
id="category"
type="select"
className="rounded-3"
placeholder="Enter Here"
>
{/* I have tried this "Value is display on the Console window but not display inside the option tag */}
{categories.map((cat) => {
<option key={cat.categoryId} value={cat.categoryId}>
{console.log(cat.categoryTitle)}
{console.log(cat.categoryId)}
{cat.categoryTitle}
{cat.categoryId}
</option>;
// debugger;
})}
{/* Also tried to use forEach loop but same result */}
{/* {catt.forEach((cat) => {
var option = cat.categoryTitle;
debugger;
<option>
{option}
{console.log(option)}
</option>;
})} */}
</Input>
</div>
<Container className="text-center">
<Button color="primary">Create Post</Button>
<Button className="ms-2" color="danger">
Reset Content
</Button>
</Container>
</Form>
</CardBody>
{content}
</Card>
</div>
);
};
The value are getting from the server and displaying into the console window but not showing inside the option tag in react
You're not returning anything from you map function.
{
categories.map((cat) => {
return (
<option key={cat.categoryId} value={cat.categoryId}>
{cat.categoryTitle}
{cat.categoryId}
</option>
);
});
}

how do i get specific data from the state on a button click in react

Here i have 3 products each have it's own addtocart button and it's option like color, size, quantity. so when i click addtocart button after selecting the options it updating the state and giving me exactly i wanted. The problem is when i selelct any product options and then i click on another product addtocart button it shows the selceted option. not the product options of the addtocart button i clicked
for example: i select the 1st product and choose it's options and i did'nt click on the 1st product addtocart button either i clicked 2nd product button but it returns 1st product selected options it should return 2nd product options.
i need to implement which ever the product button i clicks it should only return that product selected options only.it should'nt return any other product selected options.
how do i make this happen. Help me out.
function Card() {
const [items, setItems] = useState({});
const handleChageCategory = (key, event) => {
setItems((oldState) => ({ ...oldState, [key]: event.target.value }));
};
const submitHandler = () => {
console.log(items);
setItems({});
};
return (
<div className="main-container">
<div className="container">
<div className="image-container">
<img
src="https://images.pexels.com/photos/9558601/pexels-photo-9558601.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1"
alt=""
/>
</div>
<h2> T-Shirt </h2>
</div>
<div className="form-conatiner">
<div className="selectors">
<p>Solid Round Neck T-shirt</p>
<select
id="color"
name="color"
required
onChange={(event) => handleChageCategory("color", event)}
>
<option>Color</option>
<option value="black">Black</option>
<option value="green">Green</option>
<option value="orange">Orange</option>
</select>
<select
name="quantity"
required
onChange={(event) => handleChageCategory("quantity", event)}
>
<option>Quantity</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<select
name="size"
required
onChange={(event) => handleChageCategory("size", event)}
>
<option>Size</option>
<option value="medium">Medium</option>
<option value="large">Large</option>
<option value="small">Small</option>
</select>
<div>
<button onClick={submitHandler}>Add to Cart</button>
</div>
</div>
</div>
{/* second product */}
<div className="container">
<div className="image-container">
<img
src="https://images.pexels.com/photos/440320/pexels-photo-440320.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1"
alt=""
/>
</div>
<h2> i-Watch </h2>
</div>
<div className="form-conatiner">
<div className="selectors">
<p>Dizo watch with amlod </p>
<select
id="2"
name="color"
required
onChange={(event) => handleChageCategory("brand", event)}
>
<option>Brand</option>
<option value="Apple">Apple</option>
<option value="Samsung">Samsung</option>
<option value="Pixel">Pixel</option>
</select>
<select
name="qantity"
required
onChange={(event) => handleChageCategory("qantity", event)}
>
<option>Quantity</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<select
name="type"
required
onChange={(event) => handleChageCategory("type", event)}
>
<option>type</option>
<option value="29mm">29mm</option>
<option value="34mm">34mm</option>
<option value="42mm">42mm</option>
</select>
<div>
<button onClick={submitHandler}>Add to Cart</button>
</div>
</div>
</div>
{/* third product */}
<div className="container">
<div className="image-container">
<img
src="https://images.pexels.com/photos/1661471/pexels-photo-1661471.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1"
alt=""
/>
</div>
<h2> Hoodie </h2>
</div>
<div className="form-conatiner">
<div className="selectors">
<p>Adidas hoodie with zip </p>
<select
id="2"
name="color"
required
onChange={(event) => handleChageCategory("color", event)}
>
<option>Color</option>
<option value="Gray">gray</option>
<option value="White">white</option>
<option value="Cyan">cyan</option>
</select>
<select
name="qantity"
required
onChange={(event) => handleChageCategory("qantity", event)}
>
<option>Quantity</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<select
name="size"
required
onChange={(event) => handleChageCategory("size", event)}
>
<option>type</option>
<option value="39(S)">39(S)</option>
<option value="42(M)">42(M)</option>
<option value="46(L)">46(L)</option>
</select>
<div>
<button onClick={submitHandler}>Add to Cart</button>
</div>
</div>
</div>
</div>
);
}
export default Card;
const [items, setItems] = useState([]);
The handleChangeCategory Logic
const handleChageCategory = (key,product, event) => {
const isExist = items.find((item)=>item.id===product.id)
let updatedItem
if(isExist){
updatedItem = {...isExist, [key]: event.target.value}
setItems((oldState)=>(oldState.map(item=>item.id===updatedItem.id ? updatedItem: item)))
}else{
updatedItem = { ...product, [key]: event.target.value };
setItems((oldState)=>([...oldState, updatedItem]))
}
};
Submit handler logic:
const submitHandler = (product) => {
const isExist = items.find(item=>item.id===product.id)
const newCartItem = isExist? isExist: product
console.log(newCartItem);
setItems((oldState)=>(oldState.filter(item=>item.id!==newCartItem.id)))
}
I know this is an expensive operation and it might hamper user experience but you wanted to do it this way. To handle add to cart, I think the good way is to open a modal or new page after clicking Add To Cart button that also solves your problem because in this way the user is not able to update one product value and click another product button.
A more efficient way to implement a product list is to use a product component for each item which accepts the product data through props. There you will implement all the logic related to the product and check the selected fields and so on.
Full code here.
So here I created Product, which has a selectedValues state that contains the values of all the selectors as an object, and also an handleAddToCart function that sends this product to the parent component function and add the product to the cart there.
Product.js:
const Product = (props) => {
const [selectedValues, setSelectedValues] = useState({});
const { name, description, img, selectors } = props;
//(`selectors` is an array of objects. Each object is a selector with an id field and an array of the `options`).
const handleChangeCategory = (e) => {
setSelectedValues((prevState) => {
return { ...prevState, [e.target.name]: e.target.value };
});
};
const handleAddToCart = () => {
props.addToCart({ name, description, img, selectedValues });//calling the function from the props.
};
return (
<div className="product">
<div className="image-container">
<img width="150px" src={img} alt="" />
</div>
<h2> {name}</h2>
<div className="form-conatiner">
<p>{description}</p>
<div className="selectors">
{selectors.map((s) => {
return (
<select
id={s.id}
name={s.id}
required
onChange={handleChangeCategory}
>
{s.options.map((opt) => (
<option value={opt}>{opt}</option>
))}
</select>
);
})}
</div>
<button onClick={handleAddToCart}>Add to Cart</button>
</div>
</div>
);
};
export default Product;
And this is the parent component, where you have the cart and the addToCartHandler function. And in the JSX you simply create a product by calling the Product component and passing the data and the selectors you want to have for this product.
App.js (parent component):
export default function App() {
//Some dummy cart.
const DUMMY_CART = [];
//Function that adds a product to the DUMMY_CART
const addToCartHandler = (product) => {
//Do some logic with the cart of course. I'm just pushing it here to show you the result.
DUMMY_CART.push(product);
console.log('Added product:', product);
console.log('Cart:', DUMMY_CART);
};
return (
<div>
{/* Product 1 */}
<Product
img="https://images.pexels.com/photos/9558601/pexels-photo-9558601.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1"
name="T-Shirt"
description="Solid round neck T-Shirt"
selectors={[
{ id: 'color', options: ['Color', 'blue', 'grin', 'yellow'] },
{ id: 'quantity', options: ['Quantity', 1, 2, 3] },
{ id: 'size', options: ['Size', 'medium', 'large', 'small'] },
]}
addToCart={(product) => addToCartHandler(product)}
/>
</div>
);
}
Hope this helps. Please check the full code to see how it works.

How to use Radio button to select a different form in each array of useFieldArray of react-hook-form

I have a basic react hook form with field array. With append the form fields replicates as expected. For each array, I want the user to choose some field to enter without entering the other. I am using radio button and useState to achieve this. However, when i change the selection in an array, the selections in the other arrays changes as well. Please how do i correct this ? Or is there a better way to achieve this functionality. Thanks in advance for your help. The code is found below. I also have codeSandbox: https://codesandbox.io/s/usefieldarray-react-hook-form-2yp3vb?file=/src/App.js:0-3753
export default function App() {
const { handleSubmit, control } = useForm({
defaultValues: {
Detail: [
{
userName: {},
officeAddress: {},
homeAddress: {}
}
]
}
});
const { fields, append, remove } = useFieldArray({
control,
name: "Detail"
});
const [checked, setChecked] = useState();
// onChange function for the address forms
const changeAddressForm = (e) => {
setChecked(e.target.value);
};
const onSubmit = async (data) => {};
return (
<div className="App">
<h1>Selecting a different form in each field array</h1>
<form onSubmit={handleSubmit(onSubmit)}>
<ul>
{fields.map((field, index) => {
return (
<li
key={field.id}
className="w3-border w3-border-green w3-padding"
>
<div>
<div className="w3-padding-large">
<label>Username</label>
<Controller
name={`Detail.${index}.userName`}
control={control}
render={({ field }) => (
<Input
onChange={(value) => field.onChange(value)}
style={{ width: 200 }}
/>
)}
/>
</div>
<div>
<Radio.Group onChange={changeAddressForm} value={checked}>
<Radio value={1}>Office address</Radio>
<Radio value={2}>Home address</Radio>
</Radio.Group>
</div>
<div className="w3-padding-large">
{checked === 1 && (
<div>
<label>Office address</label>
<Controller
name={`Detail.${index}.officeAddress`}
control={control}
render={({ field }) => (
<Input
onChange={(value) => field.onChange(value)}
style={{ width: 200 }}
/>
)}
/>
</div>
)}
</div>
<div className="w3-padding-large">
{checked === 2 && (
<div>
<label>Home address</label>
<Controller
name={`Detail.${index}.homeAddress`}
control={control}
render={({ field }) => (
<Input
onChange={(value) => field.onChange(value)}
style={{ width: 200 }}
/>
)}
/>
</div>
)}
</div>
</div>
</li>
);
})}
</ul>
<section>
<button
type="button"
onClick={() =>
append({
userName: {},
homeAddress: {},
officeAddress: {}
})
}
>
Append
</button>
</section>
</form>
</div>
);
}

Previous state rendering in Chat thread React JS

I have created a form which has two dropdowns when I select both of them and click on Add Story Button then a chat thread is added.
The problem is when I select New Intent Name and New Action Name from dropdown then the previous Action Name also gets added I don't want like that what I want is
Problem:-
One Intent Name can Have multiple Actions But not duplicate actions
I think I am not setting or mapping the state variable properly, please guide me as of where am I going wrong
While I am able to manage the 1st point I want help with the below two
My Code
import React, { useEffect, useState } from "react";
import {
Form,
Input,
Button,
Select,
Card,
Typography,
notification,
} from "antd";
import { Layout } from "antd";
const { Header, Footer, Content } = Layout;
const { Text } = Typography;
const { Option } = Select;
const CreateStory = () => {
const [form] = Form.useForm();
const [storyValue, setStoryValue] = useState("")
const [intentName, setIntentName] = useState([])
const [actionName, setActionName] = useState([])
const [valueIntent, setValueIntent] = useState("")
const [valueAction, setValueAction] = useState("")
const [results,setResults] = useState([])
const [uniqueResults,setUniqueResults] = useState([])
const storyInputValue = (e) => {
setStoryValue(e.target.value);
};
const onFinish = (values) => {
// console.log("Success:", values);
};
const onFinishFailed = (errorInfo: any) => {
console.log("Failed:", errorInfo);
};
const SelectIntentName = (valueIntent) => {
setValueIntent(valueIntent)
console.log(valueIntent)
};
const SelectActionName = (valueAction) => {
// console.log(valueAction)
setValueAction(valueAction);
setActionName(prev => [...prev,valueAction])
};
// Error Notification
const openNotificationWithIcon = (type) => {
notification[type]({
message: "intent name cannot be empty",
});
};
const addStory = () => {
// setActionName(prev => [...prev,valueAction])
results.push({
intent_name: valueIntent,
// if valueAction is already present then don't push it into the array
actions: [...new Set(actionName)]
})
const removedDup = ([...new Map(results.map((item, key) => [item["intent_name"], item])).values()])
// setUniqueResults(prev => removedDup)
setUniqueResults(removedDup)
}
console.log(uniqueResults)
return (
<div className="csi-create-story-component-page-0103CS">
<Card
title="Create Story"
className="csi-create-story-screen-card-0104SC"
size="small"
>
<Form
onFinish={onFinish}
onFinishFailed={onFinishFailed}
layout="vertical"
>
<Form.Item
label="Story Name"
name="Story Name"
rules={[
{ required: true, message: "Please input your story name!" },
]}
>
<Input
value={storyValue}
onChange={storyInputValue}
placeholder="Enter story name"
/>
</Form.Item>
<div className="csi-action-intent-box-grid-column-0126">
<Form.Item
label="Intent Name"
name="Intent Name"
rules={[
{ required: true, message: "Please select your intent name!" },
]}
>
<Select
placeholder="Select a option"
allowClear
showSearch
onSelect={SelectIntentName}
>
<Option value="intent_name_1">intent_name_1</Option>
<Option value="intent_name_2">intent_name_2</Option>
<Option value="intent_name_3">intent_name_3</Option>
</Select>
</Form.Item>
<Form.Item
label="Action Name"
name="Action Name"
rules={[
{ required: true, message: "Please select your action name!" },
]}
>
<Select
placeholder="Select a option"
allowClear
showSearch
onSelect={SelectActionName}
>
<Option value="action_name_1">action_name_1</Option>
<Option value="action_name_2">action_name_2</Option>
<Option value="action_name_3">action_name_3</Option>
</Select>
</Form.Item>
</div>
<Form.Item>
<Button type="primary" htmlType="submit" onClick={addStory}>
ADD STORY
</Button>
</Form.Item>
</Form>
</Card>
<div>
<Layout className="csi-created-story-list-screen-card-0105SLS">
<Header>{storyValue}</Header>
<Content className="csi-intent-action-content-layout-0353IA">
<div
className="csi-created-intent-action-parent-box-0237IA"
>
{uniqueResults.map((uniqueResult,index) => {
return(
<div key={index}>
<div className="csi-intent-name-left-box">
<span className="csi-intent-text-com-0245I">
<span className="csi-INTENT-text">Intent</span>
<Text>{uniqueResult.intent_name}</Text>
</span>
</div>
<div className="csi-action-name-right-box">
<span className="csi-action-text-com-0246A">
<span className="csi-ACTION-text">Action</span>
<Text>{uniqueResult.actions[index]}</Text>
</span>
</div>
</div>
)
})}
</div>
{/* <div
className="csi-created-intent-action-parent-box-0237IA"
>
{intentName.map((intentName, index) => {
return (
<>
<div className="csi-intent-name-left-box" key={index}>
<span className="csi-intent-text-com-0245I">
<span className="csi-INTENT-text">Intent</span>
<Text>{intentName}</Text>
</span>
</div>
{actionName.map((actionName, index) => {
return (
<div className="csi-action-name-right-box" key={Math.random().toString()}>
<span className="csi-action-text-com-0246A">
<span className="csi-ACTION-text">Action</span>
<Text>{actionName}</Text>
</span>
</div>
);
})}
</>
)
})}
{actionName.map((actionName, index) => {
return (
<div className="csi-action-name-right-box" key={index}>
<span className="csi-action-text-com-0246A">
<span className="csi-ACTION-text">Action</span>
<Text>{actionName}</Text>
</span>
</div>
);
})}
</div> */}
</Content>
<Footer className="csi-footer-submit-button-for-intent-action-0357">
<Button type="primary">Submit</Button>
</Footer>
</Layout>
</div>
</div>
);
};
export default CreateStory;
It is definitely because of index in key prop. React doesn't know what you want to render there.
Don't use index as key if you're going to change order of items
The main issue relies with your implementation. You can not handle it with two array of strings.
My opinion -
Use a result array which will have items as object -
{
"intent_name": "",
"actions": [] //array of string
}
whenever Add Story button is clicked give an entry to this result array. condition should be like - if intent_name already exist then -> check if object.actions already have entry -> if not then give entry.
and loop through this result array to show the intent action list.
you can also use id instead of name in terms of uniqueness.
overall if you need any clarification then please ping me.
and apologies if I miss anything. I am trying this platform new.

How can I make my div tab to disabled cannot click in

I am trying to make my some of my tab disable when editable is set to false:
Here is my following code:
const tabs = [
"Mission",
"Agreement",
"Calendar",
"Managers",
"Members",
"Invitees",
"Applicants",
];
<div className="team-management">
<div className="team-management-tabs-header">
<div className="team-management-tab-items">
{tabs.map((tab, index) => (
<div
className={
activeTab === index
? "team-management-tab-item selected"
: "team-management-tab-item"
}
key={tab}
role="button"
tabIndex={tab}
onKeyPress={() => {
return;
}}
onClick={() => setActiveTab(index)}
>
<span className="tab-item-text">{tab}</span>
<span className="tab-item-indicator" />
</div>
))}
</div>
</div>
<div className="team-management-tab-panes">
{tabs[activeTab] === "Mission" && (
<Mission
editable={editable}
teamId={teamId}
teamData={teamData}
fetchTeamData={fetchTeamData}
/>
)}
...
</div>
Here is how my page look like:
How can I add disabled to my div in this situation?
thank you for helping.
If editable was a state then you can conditionally choose to fire the setActiveTab() function (considering that is the function that enables your tab and you want it disabled ) based on the state of editable.
onClick={() => if(editable)setActiveTab(index)}

Resources