The getAttribute function is not always retrieving the attribute value - reactjs

This is the test I made in a sandbox.
If you run the code and click the 2 buttons like this: first, back, first, back ... a few times you will see at the console that the name attribute of the target event becomes null even if it was not null the first time I pressed that button.
I also attached an image with some comments in the lower right corner to clarify the behaviour.
This is the code:
handleSearchChange(event) {
const target = event.target;
const name = target.getAttribute("name");
console.log("Test name " + name + "\n");
}
render() {
return (
<div>
<div style={{ height: "30px", width: "30px" }}>
<FirstSVG name="first_page" onClick={this.handleSearchChange} />
</div>
<div style={{ height: "30px", width: "30px" }}>
<BackSVG name="back_page" onClick={this.handleSearchChange} />
</div>
</div>
);
}

The <svg> element will only be the target if it's the innermost item clicked on. If you click on the <path> part of the SVG, for example, that'll be the target instead.
While you could fix it by using event.currentTarget instead - which will reference the element the listener was attached to instead of the element the event was dispatched to - since this is React, a much better approach would be not to pass information through the DOM at all, and to instead convey it through JavaScript alone.
For example, you could have something like:
makeHandler(name) {
return () => {
console.log(name);
};
}
render() {
return (
<div>
<div style={{ height: "30px", width: "30px" }}>
<FirstSVG onClick={makeHandler("first_page")} />
</div>
<div style={{ height: "30px", width: "30px" }}>
<BackSVG onClick={makeHandler("back_page")} />
</div>
</div>
);
}

The target property refers to the dom element on which the event originated
so you have to use currentTarget just like this:
handleSearchChange(event) {
const target = event.currentTarget;
const name = target.getAttribute("name");
console.log("Test name " + name + "\n");
}
if you would like to know more about difference between target and currentTarget see this
here
or here

Related

How to set up an image in an div using backgroundImage?

I have an div in which I have to display a photo using backgroundImage.
I have an object in which an as URL path for the image which I later on return from the obect.
I made the following code :
import React from 'react'
import './user.css';
const User = ({name,img,position,names}) => {
return ( <div className = 'card'>
<div id='image' style={{
backgroundImage: `url({${img}})`
}}></div>
<p className = 'card_name' id = 'based'>{name}</p>
<p className = 'card_position'>{position}</p>
<p>{names + ' '}</p>
</div>
);
}
export default User;
When I try to run the page I dont have any errors but my image does not appear on the page. What my seem to be the problem?
You have extra parentheses on the value you are passing to the URL, Try this:
You also need to add a height and width to the div
style={{
height: "200px",
width: "100%",
backgroundImage: `url(${img})`,
}}

React, how can i change the button colors separately in every buttons?

i would like to change the button color, but i cant do it separately, i cant to use the color switch in every button one by one!
here is my code:
const [colorSwitch, setColorSwitch] = useState(true);
const slots = ['Slot 1', 'Slot 2'];
function SlotButton({ type, colorSwitch }: { type: string; colorSwitch: boolean }) {
return (
<Button
variant={colorSwitch ? 'contained' : 'outlined'}
style={{ width: 240, height: 100, borderRadius: 20, margin: 15 }}
onClick={() => {
setColorSwitch(!colorSwitch);
}}>
{type}
</Button>
);
}
return (
<MainSection>
<DrawerHeader />
<div>
<h1 style={{ color: 'white' }}>Dryer Module</h1>
</div>
<CardBox>
{slots.map((item) => {
return <SlotButton key={item} type={item} colorSwitch={colorSwitch}></SlotButton>;
})}
</CardBox>
</MainSection>
);
} ```
You're currently using the same state variable (colorSwitch) for both buttons, thus they will always have the same state/color. As soon as one button is clicked, the state/variable is updated and both buttons are being re-rendered with the same updated state/variable.
You need to create separate state variables if you want them to behave differently. In a simple component like this I would use a state object that contains the different variables/values. This is one way you could do that:
// object with properties instead of one variable
const [colorSwitchObj, setColorSwitchObj] = useState({
"Slot 1": true,
"Slot 2": true,
});
const slots = ['Slot 1', 'Slot 2'];
function SlotButton({ type, colorSwitch }: { type: string; colorSwitchObj: boolean }) {
return (
<Button
{ /* colorSwitchObj[type] accesses matching property */}
variant={colorSwitchObj[type] ? 'contained' : 'outlined'}
style={{ width: 240, height: 100, borderRadius: 20, margin: 15 }}
{ /* update by first copying the old state and then updating the specific value */}
onClick={() => {
setColorSwitchObj(...colorSwitchObj,
colorSwitchObj[type] = !colorSwitchObj[type]);
}}>
{type}
</Button>
);
}
const Main = () => {
//...
return (
<MainSection>
<DrawerHeader />
<div>
<h1 style={{ color: 'white' }}>Dryer Module</h1>
</div>
<CardBox>
{slots.map((item) => {
return <SlotButton key={item} type={item} colorSwitchObj={colorSwitchObj}></SlotButton>;
})}
</CardBox>
</MainSection>
);
}
When your components are simple like this it's usually fine to do it with a single useState object. However, once you start working with larger objects in your state, it makes sense to look into & learn the useReducer hook. Although this hook is slightly more complex, and takes a bit more effort to create, it can prevent many annoying state bugs because you get a much better organized way of maintaining your state.
There is little need for mapping here. It would be far easier to group both buttons together in their own Component:
function SlotButtons(isSwitched) {
return (
<Button
variant={colorSwitch ? 'contained' : 'outlined'}
style={{ width: 240, height: 100, borderRadius: 20, margin: 15 }}
onClick={() => {
setColorSwitch(!colorSwitch);
}}>
{type}
</Button>
<Button
variant={!colorSwitch ? 'contained' : 'outlined'}
style={{ width: 240, height: 100, borderRadius: 20, margin: 15 }}
onClick={() => {
setColorSwitch(!colorSwitch);
}}>
{type}
</Button>
)
}
The second button reacts to inverted value of the first one. Or you could send both states to that component/

How to set focus on a custom filter dialog in ag-grid react?

We're developing a grid that will be used with screen readers. So far ag-grid is pretty accessible, but one issue is setting the focus on a custom filter when it's opened. (Note, the built in filters do set the focus correctly.)
Previous versions of the grid had a function "afterGuiAttached()" that could be used to set the focus after render. But we're using ag-grid-community 25.1.0 and ag-grid-react 25.1.0 and that function no longer exists.
Here is a plunker example and I've pasted a sample custom filter below.
Plunker Example
import React, {
forwardRef,
useEffect,
useImperativeHandle,
useState,
useRef,
} from 'react';
export default forwardRef((props, ref) => {
const [filterText, setFilterText] = useState(null);
// expose AG Grid Filter Lifecycle callbacks
useImperativeHandle(ref, () => {
return {
doesFilterPass(params) {
// make sure each word passes separately, ie search for firstname, lastname
let passed = true;
filterText
.toLowerCase()
.split(' ')
.forEach((filterWord) => {
const value = props.valueGetter(params);
if (value.toString().toLowerCase().indexOf(filterWord) < 0) {
passed = false;
}
});
return passed;
},
isFilterActive() {
return filterText != null && filterText !== '';
},
getModel() {
return { value: filterText };
},
setModel(model) {
setFilterText(model.value);
},
};
});
const onChange = (event) => {
setFilterText(event.target.value);
};
useEffect(() => {
props.filterChangedCallback();
}, [filterText]);
return (
<div style={{ padding: 4, width: 200 }}>
<div style={{ fontWeight: 'bold' }}>Custom Athlete Filter</div>
<div>
<input
style={{ margin: '4 0 4 0' }}
type="text"
value={filterText}
onChange={onChange}
placeholder="Full name search..."
/>
</div>
<div style={{ marginTop: 20 }}>
This filter does partial word search on multiple words, eg "mich phel"
still brings back Michael Phelps.
</div>
<div style={{ marginTop: 20 }}>
Just to emphasise that anything can go in here, here is an image!!
</div>
<div>
<img
src="https://www.ag-grid.com/images/ag-Grid2-200.png"
style={{
width: 150,
textAlign: 'center',
padding: 10,
margin: 10,
border: '1px solid lightgrey',
}}
/>
</div>
</div>
);
});
I guess I'm late to the question, but I was facing the same issue, and I found a workaround. I am using ag-grid community v26.2.0. And the way I solved it is below.
Basically, you give your input an ID and on the onFilterOpened event, you do a direct focus on the DOM element itself. Of course you could add a small delay using setTimeout() if you have some animation set up while the popup is entering in the DOM.
<AgGridReact
{...otherGridOptions}
onFilterOpened={() => document.querySelector("#idOfYourInput")?.focus()}>
//columns or other children
</AgGridReact>

React Array.map rendering multiple components with events and how to access individual components

This is my first question. So greetings :)
I am, first of all, looking for good documentation or examples concerning this question. Perhaps, as I am new to reactjs, there is a canonical way to do this that im not familiar with. Any links to relevant material gratefully received.
So, what i want to do is very simple: array.map to render multiples of this component (a very simple blue box) which changes to gray when 'status' is clicked.
The problem I encounter is that clicking one box changes them all! I am absolutely sure this is quite a beginner problem. I have tried a lot of different experiments with 'ref' or using somehow trying to pass the key into statusClick().
There is an onClick attribute in the second tag in the jsx.
How can it work so that in the rendered components from array.map, only the individual box behaves as described?
Here is the (messy) code.
constructor(props) {
super(props);
this.state={colour:'DeepSkyBlue', status: 'active'}
this.statusClick=this.statusClick.bind(this)
};
statusClick(){this.state.status==='active' ? this.setState ({colour:'Gray',status:'inactive'}) :this.setState ({colour:'DeepSkyBlue', status:'active'})};
render(){return(
namesArray.map((namey,index) =>
{return (
<div key={index} style={{backgroundColor:this.state.colour, height:'180px',width:'160px',display:'inline-block',margin:'20px' }}>
<p style= {{textAlign:'center',color:'white',fontSize:'20px'}}><br/>{namey}<br/>
{platArray[index].join()} <br/> </p>
<p style= {{textAlign:'center',color:'white',fontSize:'20px', border:'5px'}} onClick={this.statusClick}>{this.state.status}</p>
</div>
)
}
)
)
};
}```
Thank you very much for any input.
Issue
You've only a single state.color value that you are applying to every element you map, so when it is toggled it is toggled for all of them.
Solution
You should instead store a map, or dictionary of the elements you want to be "active" and use the alternative color.
So long as the namesArray isn't mutated (i.e. it doesn't have items added to or removed from, and isn't sorted) then the array index is ok to use as a React key.
Instead of of storing the color or status, store an object of active indices.
this.state = {
// ... other state
activeIndices: {},
};
Update the statusClick handler to consume an array index.
statusClick = index => {
this.setState(prevState => ({
activeIndices: {
...prevState.activeIndices,
[index]: !prevState.activeIndices[index],
},
}));
};
And check the activeIndices state when mapping the elements to know what color style to apply. Pass the index to the statusClick handler
return namesArray.map((namey, index) => {
return (
<div
key={index}
style={{
backgroundColor: this.state.activeIndices[index] ? 'Gray' : 'DeepSkyBlue',
height: "180px",
width: "160px",
display: "inline-block",
margin: "20px"
}}
>
<p style={{ textAlign: "center", color: "white", fontSize: "20px" }}>
<br />
{namey}
<br />
{platArray[index].join()} <br />{" "}
</p>
<p
style={{
textAlign: "center",
color: "white",
fontSize: "20px",
border: "5px"
}}
onClick={() => this.statusClick(index)} // <-- pass index
>
{this.state.status}
</p>
</div>
);
});

Linking to a different page using react-day-picker and react router v4

I'm trying to utilize this example in order to create a calendar that lists out the events in the current month, I have this part working, but what I have yet to figure out is how to make it so that the user can click the event name and it would take them to that event page.
So per that example, if they click on one of the birthdays, it would take them to an events page where they could see more about that birthday.
Currently, my events page is being rendered using this function:
renderEvents() {
const {events} = this.state
this.state.events = {};
let eventItems = this.state.eventGet.map(event => {
console.log(event.id)
if(typeof(events[moment(event.date).date()]) !== "undefined") {
events[moment(event.date).date()].push(event.name)
} else {
events[moment(event.date).date()] = [event.name]
}
});
function renderDay(day) {
const date = day.getDate();
const dateStyle = {
position: 'absolute',
color: 'lightgray',
bottom: 0,
right: 0,
fontSize: 20,
};
const containerStyle = {
margin:'2px',
border: '1px solid #3a87ad',
borderRadius: '3px',
position: 'relative',
display: 'block',
cursor: 'pointer'
};
const textStyle = {
fontSize: '0.8em',
textAlign: 'left',
margin: '1.5px',
}
const cellStyle = {
height: 150,
width: 160,
position: 'relative',
};
return (
<div style={cellStyle}>
<div style={dateStyle}>{date}</div>
{events[date] &&
events[date].map((name, i) => (
<div onClick={() => this.props.history.push('/organizations/' + this.props.match.params.orgID + '/events' + i)} key={i} style={containerStyle}>
<div style={textStyle}> {name} </div>
</div>
))}
</div>
);
}
return (
<div>
<Grid component="section" className="section--center" shadow={0} noSpacing>
<Cell col={12}>
<FABButton style={{margin: '10px', float: "right"}} colored ripple onClick={() => this.props.history.push('/organizations/' + this.props.match.params.orgID + '/events')}>
<Icon name="add" />
</FABButton>
</Cell>
<DayPicker
canChangeMonth={true}
className="Birthdays"
renderDay={renderDay}
/>
</Grid>
</div>
);
}
The current problem is within the sub-function, renderDay which is called by the DayPicker component that gets the events associated with the day. When I try to push to the history property, it errors out and says that I cannot read property 'history' from undefined, which makes sense because we did not pass the history property to that function.
Can someone help me in figuring out how to modify that sub-function so that the onClick event on the div will take a user to that events page?
and says that I cannot read property 'history' from undefined
Make sure your renderDay function is bound to the correct this:
<DayPicker
canChangeMonth
className="Birthdays"
renderDay={renderDay.bind(this)}
/>

Resources