I have a div mapped so it would render any number of times according to data sent from the database. And on componentDidMount i'm getting an id. I want to change background color of the div which matches the id i'm getting from componentDidMount How can i do it?
my code
componentDidMount() {
alert(this.props.voted_id);
}
render() {
let {contents, submitvote, postId, voted_id} = this.props
return (
<div className="txt_vote_bar_div" id={contents.post_poll_content_id} style={{backgroundColor: this.state.vote_status ? '#0b97c4' : 'white'}}>
<p className="txt_vote_choice" style={{color: this.state.vote_status ? '#FFFFFF' : '#6a6a6a'}}
onClick={() => {
this.handleClick(contents.post_poll_content_id);
}}> {contents.content} </p>
<p className="txt_tot_votes"
style={{color: this.state.vote_status ? '#FFFFFF' : '#6a6a6a'}}> {contents.votes}%
(Votes:)</p>
</div>
);
};
Basically what i want to do is if this.props.voted_id matches contents.post_poll_content_id , i want to change the background color of that div using states.
You can simply get the element and change its style.
componentDidMount() {
let el = document.getElementById(this.props.voted_id);
if(el) {
el.style.backgroundColor = "blue";
}
}
Update
Above approach manipulates DOM directly. It would be wise to let React handle actual DOM manipulations, unless absolute necessity.
To let React take care, you can make changes in JSX as:
<div className= {voted_id===content.post_poll_content_id ? "txt_vote_bar_div active" : "txt_vote_bar_div"} id={content.post_poll_content_id} >
/CSS
.active {
background-color:#0b97c4;
}
Basically this.props.voted_id inside componentDidMount should equal to this.props.voted_id inside render.
How about
style={{backgroundColor:voted_id===contents.post_poll_content_id ? '#0b97c4' : 'white'}}
Related
I have a requirement that need to update the style of the element based on dynamic content. Here i am setting ul element max height based on detail content.
if (element.innerText === this.res.viewMore) {
primaryButtonText = this.res.viewLess;
messageListElement.style.maxHeight = `${this.checkHeightOfList(
detail.split(','),
)}px`;
} else {
primaryButtonText = this.res.viewMore;
messageListElement.style.maxHeight = null;
}
What is the best practice to update the max height in react js ?
Here is my DOM
The best practice to update a style attribute in react is to do something like this:
const maxHeight = someCondition ? null : '100px';
And to apply this max height, do:
<div style={{max-height: maxHeight}}>my list</div>
If you want the max-height to dynamically update, store maxHeight as a state variable and update it accordingly using setState.
Your example code has a strong javascript approach. In React you would store your state in a variable (viewState in my example), and you render styling conditionally with this variable. Your code could look something like this:
const yourDetailArray = ["foo", "bar", "test", "view"];
const List = () => {
const [viewState, setViewState] = React.useState(false);
const checkHeightOfList = () => {
// Setting the max height to 10px * length of array to show effect
if (viewState) return yourDetailArray.length * 10 + "px";
else return null;
};
return (
<React.Fragment>
<ul style={{ maxHeight: checkHeightOfList(), border: "1px solid grey" }}>
{yourDetailArray.map((item) => (
<li>{item}</li>
))}
</ul>
<button onClick={() => setViewState(!viewState)}>
{viewState ? "View less" : "View more"}
</button>
<p>State is currently : {viewState ? "true" : "false"}</p>
<p>Max height is currently: { checkHeightOfList() || 'null' }</p>
</React.Fragment>
);
};
// Render it
ReactDOM.render(
<List />,
document.body
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
There are ways to optimise this code, but hopefully it shows the concept. Please note that this example is using functional components which might differ in your code.
In normal JavaScript you can grab an element by its id and add a style to it.
For example:
var element = document.getElementById("myElement");
element.style.backgroundColor = "#f5f5f5";
My question is how you can do this in react. Is it even possible to add this style?
In react im using onChange in a function outside the render(). I looked at the React DOM for styling and tried but since styling is in different function it will tell me how the variable is undefined.
this is my code:
ChangeImage() {
var imgStyles = {
backgroundColor: '#000',
padding: 5,
}
}
render() {
return (
<div className="class">
<div className="img-surround">
<img
src={this.state.file}
id="img"
style={imgStyles}/>
</div>
Everything is working except styles and I even tried putting in different functions
If you want to render the element with the style you can return the element like this in a react functional component:
return <div style={{backgroundColor: "#f5f5f5"}}></div>
If you want the element to only have that style in a certain condition you can use the useState hook in a react functional component:
const [myState, setMyState] = useState(false);
return <div style={myState && {backgroundColor: "f5f5f5"}}></div>
And you should change myState's value using setMyState however you like. For example:
const [myState, setMyState] = useState(false);
return <div onClick={() => myState ? setMyState(true) : setMyState(false)} style={myState && {backgroundColor: "f5f5f5"}}></div>
In this example whenever you click on the div the style is added or removed by case
I'm mapping through an array and creating <li> elements for each element of the array. There is also an <img> inside each <li> element which has a display attribute whose value is depending upon the isDownloading state object.
The problem:
Whenever I'm clicking an <li> element the images appear for all of the <li> elements instead of that particular <li> element.
The code:
...
constructor() {
super()
this.state = {data: null, isDownloading: null}
}
componentDidMount() {
...
//array of data from api call
this.setState({data: dataArray})
}
generateList = (data) => {
const listElement = data.map((info, i) => {
return (
<li onClick={() => this.setState({isDownloading: true})} key={i}>Download<img style={{display: this.state.isDownloading ? 'inline-block' : 'none'}} src='loader.png' /></li>
)
}
return listElement
}
render() {
return (
<div>
<ul>
{this.generateList(this.state.data)} //renders 10 li elements
</ul>
</div>
)
}
...
It is because for every img you are doing this :
style={{display: this.state.isDownloading ? 'inline-block' : 'none'}}
You are referencing the same state for each image. If this.state.isDownloading every image's style will be : { 'inline-block'} since this.state.isDownloading is true.
Now one way to display a loader per element is to update your state so that you will know if a download is happening (isDownloading) AND to which img it refers to. So you must track two things :
is downloading ?
which one is downloading ?
You can do that by updating your 'onClick' function, we can for exemple pass the 'key' props as an argument so that we know which li is being downloaded :
<li onClick={() => this.setState({ isDownloading: true, keyItemBeingDownloaded: key})/>
And now you can update your style's condition to this :
this.state.isDownloading && key === this.state.keyItemBeingDownloaded
So now, if isDownloading && if the item's key being downloaded corresponds to the item's key in the loop then display the loader.
I'm facing a problem with reactjs.
I need to compare screen height with a div in order to set the max-width of it.
No problem with the screen size, the problem is that the div I want to check is inside a map. Thus, this line always return null:
componentDidMount() {
test2 = document.getElementById("coucou");
console.log(test2);
}
which refers to:
render() {
return (
<div>
{this.props.data.map((value, i) => {
return (
<div key={i}>
{
this.state.activeIndex === 0 ?
<div id="coucou" style={{width: '75%', margin: '0 auto'}}>
<img
className={"image-detail"}
style={{maxWidth: '80%', borderRadius: '12px'}}
src={imgBaseUrl + value['mainImage']}
/>
</div>
}
</div>
);
}
</div>
);
}
So, I can't access the id nor the class of the div.
Any idea why? And how could I have access to those elements?
Ps: the code showing is a part of the whole, the map deals with other condition. Nevertheless, I checked it, the id is unique to this div / image.
But the problem is the same with a getElementsByClass => null
React works by working on a "virtual" DOM before rendering to the real DOM, so unless your test statements are executed after a call to ReactDOM.render, the result is to be expected, the element is still in React's virtual DOM.
Assuming you placed the test code in the correct place, in .componentDidMount for instance, have you tried logging this.props.data directly? It may simply be empty in your case.
Moreover there's also a little problem with your code since Array#map produces an array of values, there can be multiple divs with the id "coucou", and that isn't valid HTML.
If there a reason why you are not following best practice and using a ref?
return (
<div>
{this.props.data.map((value, i) => {
return (
<div key={i}>
{
this.state.activeIndex === 0 ?
<div ref={n => (this.coucouRef = n)} style={{width: '75%', margin: '0 auto'}}>
<img
className="image-detail"
style={{maxWidth: '80%', borderRadius: '12px'}}
src={imgBaseUrl + value['mainImage']}
/>
</div>
}
</div>
);
}
</div>
);
And access via this.coucouRef.
Also note that you don't need to wrap static strings in braces - className="image-detail" will do.
Also, it is bad practice to assign an array index as the key in your loop - try and use something unique to that item, such as an ID which does not change according to the order the array is in.
I have a list of elements created by using an array in react. On user click how can I make the clicked element active (by adding a CSS class) while making the other elements inactive (by removing the active class)?
My rendering of elements looks like this.
{this.props.people.map(function(person, i){
<div className='media' key={i} onClick={state.handleClick.bind(state,i,state.props)}>
<item className="media-body">{person.name}</item>
</div>
}
When the user clicks on one of these elements an active class will be added to the clicked 'media' element making the clicked element 'media active' while removing the 'active' class from the previously clicked element??
constructor(props) {
super(props);
this.state = { activeIndex: 0 };
}
handleClick(index, props) {
// do something with props
// ...
// now update activeIndex
this.setState({ activeIndex: index });
}
render() {
return (
<div>
{
this.props.people.map(function(person, index) {
const className = this.state.activeIndex === index ? 'media active' : 'media';
return (
<div className={className} key={index} onClick={handleClick.bind(this, index, this.props)}>
<item className="media-body">{person.name}</item>
</div>
);
}, this)
}
</div>
);
}
For the sake of clean code I personally would suggest you creating subcomponents to add functionality to mapped elements.
You could create a small subcomponent which simply returns the element which you want to add functionality to just like this :
...
this.state = {
active: false
}
...
return(
<div className=`media ${this.state.active ? 'active' : ''` onClick={()=>{this.setState({active: true})}}>
<item className="media-body">{this.props.name}</item>
</div>
)
...
And in your map function you simply pass the contents as properties:
{this.props.people.map(function(person, i){
<SubComponent key={i} {...person} />
}
That way you stay with clean code in the "root" component and can add complexity to your subcomponent.
In your handleClick method you could store in the component's state the clicked person (looks like the collection is of people). Then set the className conditionally based on, say, the person's id.
You can set the className using something like:
className={this.state.clickedPersonId === i ? 'media media--clicked' : 'media'}
(NB This is using i, the index of the item in the people array; you may want to use something a little more explicit, like the person's real Id.)