onClick doesn't work on custom created element - javascript

Greetings
I have built a search and every time user types word it renders new checkboxes but new checkboxes don't work like they used to be none of the event listeners work on new checkboxes, when I'm clicking on checkboxes they just don't react, but in old ones, until search will render this they are working normally
//search in checkbox data
const checkOptions = (container, value, containerId) => {
for (let i = 0; i < props.unique[containerId].length; i++) {
let item = props.unique[containerId][i];
if (
props.unique[containerId][i] !== null &&
props.unique[containerId][i].includes(value)
) {
element = (
<label
onClick={(e) => {e.stopPropagation(); ifAnyChecked(e);}} key={i}>
<input onClick={(e) => {tableSearch(e);}} type="checkbox" value={item ? item : "empty"}/>
{item && item.length > 28 ? (
handleCheckbox(item)
) : (
<p>{item}</p>
)}
</label>
);
tempData += ReactDOMServer.renderToString(element);
}
}
container.innerHTML = tempData;
};
any idea what's happening?

Have you tried to use onChange event instead of onClick? As far as I know, input type checkbox doesn't have such an event like onClick.
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox

I used to get this problem when I was working with Vanilla JS whenever i render a new element then that element was not triggering my events. That was because they were generated on runtime so the event wasn't bound to that element. Now I think that thing is happening here as well. So I changed your code and put it inside a state now it is working. I hope I helped. Do let me know if this is not the solution that you were looking for but it solves your problem though
I put the html inside a state array then i mapped it out inside the newCheckBox div. I changed the input to controlled input with fieldValue state. Lastly i changed the new checkbox alert from onClick={alert("doesn't goes in")} to onClick={() => alert("I think its working now right?")}
Here is the complete code sandbox
https://codesandbox.io/s/polished-sea-vedvh?file=/src/App.js

Related

Pass onClick to each image using map function

I am looking to pass an onClick function to each image element created using the map function. So that when a user clicks the thumbnail image it changes the main image to the thumbnail that was clicked.
Right now however it seems like the onClick function is being called without even being clicked and the image that is displayed is the last image in the array.
Also when I click the images nothing happens.
I feel like there may be multiple issues but not sure what.
let currentIndex = 0;
let changeImage = function(index) {
currentIndex = index;
}
const gallery =
product.images.length > 1 ? (
<Grid gap={2} columns={5}>
{product.images.map((img, index) => (
<GatsbyImage
image={img.gatsbyImageData}
key={img.id}
alt={product.title}
index={index}
onclick={changeImage(index)}
/>
))}
</Grid>
) : null;
The above code is affecting the below code.
<div>
{console.log('index is: ' + currentIndex)}
<GatsbyImage
image={product.images[currentIndex].gatsbyImageData}
alt={product.title}
/>
{gallery}
</div>
add the arrow function to the syntax like this ,
change onclick={changeImage(index)}
to this onclick={()=>changeImage(index)}
and for the rerendering .
i think you need to use state instead of let
change let currentIndex = 0;
to
const [currentIndex,setCurrentindex]=useState(0)
and currentIndex = index;
to setCurrentindex(index)
we use State to rerender the dom whenever there is a change , in your case the dom is not rerendering because you are not using state .
that should solve your problem

How to add a class of "active" to individual elements in a functional navigation component in react?

This is something quite simple but somehow resulted in a crazy rabbit hole.
This link shows what I want:
https://www.w3schools.com/howto/howto_js_active_element.asp
Nothing special, now the thing becomes hairy for me when the elements in the navbar are rendered from an array of objects (from the specs). The approach I am following is basically rendering a list of buttons, this list of buttons is the state, since supposedly when you update a state it triggers a re-render, then when a button is clicked it "sets" the active class to false on the entire array-state then activates it only for the clicked one. So far it works.
The problem is that the active class is rendered two steps behind. One for the moment when the class in the array-state's elements are set to false, the other when the clicked element gets updated.
As far as I understand useState and setState are queues, hence those are applied asynchronously on each render, in order to avoid that and get the renders to show the current state, useEffect is utilized.
Now the thing is that I am not sure how to apply useEffect in order to achieve the immediate render of the "active" class.
This is the code I have:
import { options } from 'somewhere...'
export default function SideMenu(props){
let auxArr = []
let targetName
const [stateOptions, setStateOptions] = useState([...options])
const [currentOption, SetCurrentOption] = useState({})
function activeOption(e){
// this helps with event bubbling
if (e.target.tagName == "P" || e.target.tagName == "SPAN"){
targetName = e.target.parentElement.id
} else if (e.target.tagName == "IMG"){
targetName = e.target.parentElement.parentElement.id
} else {
targetName = e.target.id
}
// since the main state is an array of objects I am updating it
// in three steps, first the current object is "activated"
// then the main array-state gets "inactivated" to erase all
// the previous "active" classes, finally the activated object
// replaces the corresponding inactive object in the main state.
let targetElement = stateOptions.filter(e => e.id==targetName)[0]
SetCurrentOption({
id: targetElement.id,
activity:true,
img: targetElement.img,
name: targetElement.name
})
// first the "classes" are set to false, then the
// "activated" object replaces the corresponding one
// in the main object, from here comes the two
// steps delay.
auxArr = [...stateOptions]
auxArr.forEach(e => e.activity=false)
setStateOptions(auxArr)
const newOptions = stateOptions.map(e =>
e.id==currentOption.id ? currentOption : e
)
setStateOptions(newOptions)
}
return(
<aside className={styles.sideDiv}>
<nav>
{stateOptions.map(({id, img, name, activity, link}) => {
return(
<button key={id} id={id} onClick={activeOption} className={activity?styles.active:""}>
<Image src={img}/>
<p className={timeColor.theme}> {name} </p>
</button>
)
})}
</nav>
</aside>
)
}
Thanks in advance for any help you can provide.

why tooltip is not hide on mouseleave event in react?

I am trying to show tooltip on mouse enter and hide on mouse leave.first i make a simple demo which is working fine.
https://codesandbox.io/s/exciting-shannon-4zuij?file=/src/list.js
above code working fine on hover it show's the tooltip and hide on leave.
see same concept i apply on a application.(this code is not working)
https://codesandbox.io/s/cool-liskov-8rvjw?file=/src/App.js
when I hover a item is show the tooltip.but it is not hiding the tooltip when you leaving the item.something went wrong.
const Student = ({students,clickHandler}) => {
console.log(students,"---ee")
const [selectedStudent,setSelectedStudent] = useState(null)
const onMouseHandler = (student,e)=>{
student.visibility = true
setSelectedStudent(student)
}
const onMouseLeaveHandler = (student)=>{
console.log('======',student)
student.visibility = false
setSelectedStudent(student)
}
return (
<ul className="student-container">
{
students && students.length > 0 ? students.map((student,index)=>{
return (
<li key={index} onClick={()=>{
clickHandler(student)
}}
onMouseLeave={()=>{
onMouseLeaveHandler(student)
}}
onMouseEnter={(e)=>{
onMouseHandler(student,e)
}} style={{position:'relative'}}>
<a><span>{student.name}</span></a>
{student.visibility? <ToolTip showToolTip={student.visibility} selectedStudent={selectedStudent}/>:null}
</li>
)
}):null
}
</ul>
);
};
export default Student;
Step too reproduce
Hover on first item Raj
and then try to hover sameer.both tooltip will display.I want only one tooltip will be display which is hovered.
I want my handlers should be in my functional component . I don't want to move these handler to parent component and pass handler as a props
In your demo it's also not work well, - one hide only when open another.
when you set student.visibility you not set state, so nothing has rerendered.
Then when you call setSelectedStudent you pass there just the same referance as was before, since it's the same object, so the state not changed, and again - nothing got rerendered.
What you have to do is pass the updated student in a new variable. like so:
setSelectedStudent({...student})
Then all should work

How to ensure state is not stale in React hook

The buttons i create using below seems to lag in the selectedButtonIdx value.
Is the toggleSelected not complete by the time getClass is called ?
function ButtonGroup(props) {
const [selectedButtonIdx,setIdx]=useState(props.loadCurrentAsIndex);
const toggleSelected = (e) => {
setIdx(parseInt(e.target.dataset.index));
props.onclick(e);
};
const getClass = (index) => {
return (selectedButtonIdx === index) ? classnames('current', props.btnClass)
: classnames(props.btnClass)
};
let buttons = props.buttons.map((b, idx) => <Button key={idx} value={b.value} index={idx} text={b.text}
onclick={e => toggleSelected(e)}
btnClass={getClass(idx)}/>);
return (
<div>
{buttons}
</div>
);
}
Every onclick is expected to show the user which button in the group was clicked by changing its class.
By looking at this,
<Button
key={idx}
value={b.value}
index={idx}
text={b.text}
onclick={e => toggleSelected(e)}
btnClass={getClass(idx)}
/>
Button is your custom component,
Two things to notice here,
You have provided onclick (c is small) props, in you actual component it should be onClick={props.onclick}
You have used e.target.dataset.index, to work with dataset we should have attribute with data- prefix. So your index should be data-index in your actual component.
So finally your Button component should be,
const Button = (props) => {
return <button text={props.text} data-index={props.index} onClick={props.onclick} className={props.btnClass}>{props.value}</button>
}
Demo
The function setIdx, returned from useState is asynchronous, this means that it may be not be finished by the time you run your next function (as you guessed).
Take a look at useEffect it allows you to specify a function to run once an item in your state changes, this method will ensure your functions are called in the right order.
By now I don't see anything wrong here.
How it works:
initial render happens, onClick event listener is bound
user clicks a button, event handler calls setIdx triggering new render
new render is initiated, brand new selectedButtonIdx is used for rendering(and for getClass call as well)
See, there is no reason to worry about if setIdx is sync function or async.

Dynamically adding input fields and keeping track of what was entered

I am wanting to dynamically create input field values for each category a user creates, the issue is how can I keep track of what the user enters into the input field. As I cannot create X amount of states as it is dynamic. Any tips would be much appreciated, my code is shown below:
var categories = newData.map((category,index) => {
console.log(category)
return (
<div className="content row marginCenter" key={category._id}>
<p>{category.category}</p>
<input type="text" /> //How do I keep track of what was entered for this input field??
<button onClick={() => this.addCategoryLink(category._id)}>Add
link</button>
</div>
)
})
I am wondering how to bind that to the button element
The React docs have a section related to the core of this question:
https://reactjs.org/docs/handling-events.html#passing-arguments-to-event-handlers
Assuming your state holds an array of "categories" objects- essentially, I think what you're looking for boils down to something like this in your map function:
{this.state.categories.map(category => (
<input
type="text"
onChange={event => this.handleCategoryChange(category, event)}
value={category.value}
/>
)}
And then a change handler that looks something like this:
handleCategoryChange = (category, event) => {
const value = event.currentTarget.value;
this.setState(state => {
// Create a copy of the categories array:
const categories = [...state.categories];
// Create a copy of the category, with an updated value:
categories[category.index] = {
...category,
value
};
// Update state with the new values:
return { categories };
});
};
Here's a simple demo:
https://codesandbox.io/s/woqpwvl777
i have other Way for doing this , Of course this way just working well in some situation , forExample when you have just 1 or 3 value
i think you wanna create Input , and there Input are dynamic , and you want define that , if user click in first Button , you get and use first TextInput (value)
in my way ( again i say this : this way just well in some situation ) , we Create data Json like this
[
{ id: n ,
category: 'some',
value: ''
}
in this structure Value key , in the mounting contain nothing or null value if the Value not defined before
for now i create one handler method and this method, called after onChange Event fired on
<input onChange={(e) => this.getValue(category.id,e)} />
that element , this means when user start fill input onChange event handle function and update your state
getValue(id,e) {
let thisId = id-1;
let vs = this.state.c;
vs[thisId].value = e.target.value;
this.setState({
c:vs
});
let v = this.state.c[thisId];
console.log(v);
}
i create Pen in this address -> https://codepen.io/hamidrezanikoonia/pen/vRyJRx?editors=1111
you can check console , for more details ( open console tab in codepen )
and for more details , i create two method , the first fired when input (text) filled ( onChange event ) , and the other fired when clicked on button ( click event )

Categories

Resources