Is it possible to have a dynamic ref in react js? - reactjs

Is it possible to have a dynamic ref in react js.
I want to assign a dynamic value in ref of span.
I dont have any idea how to call the dynamic ref.
class Test extends React.Component {
constructor() {
super();
}
hide = () =>{
const span = this.refs.ref-1; // I dont have any idea how to call the dynamic ref here.
span.className = "hidden";
}
table = () =>{
//postD is something axios request;
const tableDetail = Object.keys(postD).map((i) => (
<div key={i}>
<h2>{ postD[i].title }</h2>
<p>{ postD[i].content }</p>
<span ref={"ref-"+postD[i].id} id="test">The Quick Brown Fox.</span> --> the ref is like ref="ref-1" and ref="ref-2" and ref="ref-3" so on.. it is dynamic
<button onClick={() => this.hide()} >Like</button>
</div>
}
render() {
return (
<>
<h2>Table</h2>
{this.table()}
</>
);
}
}

By updating below two methods you will get the solutions.
Pass id in hide method.
table = () => {
const tableDetail = Object.keys(postD).map((i) => (
<div key={i}>
<h2>{postD[i].title}</h2>
<p>{postD[i].content}</p>
<span ref={`ref-${postD[i].id}`} id={`test-${postD[i].id}`}>The Quick Brown Fox.</span>
<button onClick={() => this.hide(postD[i].id)} >Like</button>
</div>
));
}
Use same id to get the ref.
hide = (id) => {
const span = ReactDOM.findDOMNode(this.refs[`ref-${id}`]);
span.className = "hidden";
}
Hope this will help!

You can use below snippet. I have updated your class. Just pass "id" to the hide method.
class Test extends React.Component {
constructor() {
super();
}
hide = (id) => {
const span = this.refs[`ref-${id}`];
span.ClassName = "hidden";
}
table = () => {
const tableDetail = Object.keys(postD).map((i) => (
<div key={postD[i].id}>
<h2>{postD[i].title }</h2>
<p>{postD[i].content }</p>
<span ref={`ref-${postD[i].id}`} id={`${postD[i].id}`}>The Quick Brown Fox.</span>
<button onClick={() => this.hide(postD[i].id)} >Like</button>
</div>
}
render() {
return (
<>
<h2>Table</h2>
{this.table()}
</>
);
}
}

Related

Cannot read properties of undefined - reactjs

Please help. I am new to react and I'm trying to toggle an accordion by using state but I'm getting an error on the onClick portion. Cannot read properties of undefined (reading 'toggleAccordion'). I'm confused to what I should do.
Any help would be appreciated. Thank you!
export class Accordion extends Component {
constructor(props) {
super(props);
this.state = {
isActive: false
}
this.toggleAccordion = this.toggleAccordion.bind(this);
this.renderAccordion = this.renderAccordion.bind(this);
}
toggleAccordion = () => {
this.setState({ isActive: !this.state.isActive})
}
renderAccordion = () =>
{
const { items } = this.props;
const { isActive } = this.state;
const accordionItems = items.map(function(item) {
return (
<div>
<button className={styles.accordion} onClick={this.toggleAccordion()}>
{item.title}
<span style={{float: "right"}}>{isActive ? '-' : '+'}</span>
</button>
{isActive && <div className={styles.content} dangerouslySetInnerHTML={{__html: item.content}}></div>}
<br/><br/>
</div>
)
})
return (
{accordionItems}
)
}
render() {
return(
<React.Fragment>
{this.renderAccordion()}
</React.Fragment>
)
}
}
In React you need to provide a reference to the function in event handlers, instead of calling the function. Try (note the parenthesis) :
<button className={styles.accordion} onClick={() => this.toggleAccordion()}>
or
<button className={styles.accordion} onClick={this.toggleAccordion}>
instead of
<button className={styles.accordion} onClick={this.toggleAccordion()}>
See https://reactjs.org/docs/handling-events.html#gatsby-focus-wrapper for more details on how React handle events.

How can I change the background color of elements when they're clicked?

I have a list of div elements in a ReactJS projects. I want to just get an indication when someone clicks change the background color.
the following is the basic code.
function changetoselected(event){
// now change backgroundColor of
// event.currentTarget to white
}
<div>
bigarrayofsize100plus.map((item,index) =>{
return(
<div
className="p-2"
onClick={(e) => changetoselected(e)}
style={{backgroundColor:"green"}}
>
.....
</div>
)
}
</div>
I dont want to store in the state all the elemets uncessarily. I dont have to trace clicked items here.
If once clicks i want to just change color. How can i do it
Use the style property to set a backgroundColor like this.
function changetoSelected(event){
event.target.style.backgroundColor = '#fff'
}
You can also use Refs in React like this
For a Function Component do this
`
import { useRef } from 'react';
function MyComponent() {
const divEl = useRef(null);
const changeToSelected = () => {
divEl.current.style.backgroundColor = '#fff';
};
return (
<div ref={divEl} onClick={changeToSelected}>
...
</div>
);
}
For a Class Component do this
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.divElement = React.createRef();
}
changetoselected = () => {
this.divElement.current.style.backgroundColor = '#fff';
}
render() {
return <div ref={this.divElement} onClick={this.changetoselected}>
...
</div>;
}
}
After all, working with pure dom (by ref or event) may not be what you are searching for, you can consider using react state and apply className or style to your dom elements
import { useState } from 'react';
const MyComponent = () => {
const [backgroundColor, setBackgroundColor] = useState('green');
return (
<div
onClick={() => setBackgroundColor('white')}
style={{ backgroundColor }}
>
...
</div>
);
}
EDIT
function MyComponent() {
const divEl = useRef(null);
const changeToSelected = () => {
divEl.current.style.backgroundColor = '#fff';
};
return (
<div>
{bigarrayofsize100plus.map((item,index) =>
<ChildComp
key={index}
item={item}
>
.....
</div>
)}
</div>
);
}
function ChildComp({ item }) {
const divEl = useRef(null);
const changeToSelected = () => {
divEl.current.style.backgroundColor = '#fff';
};
return (
<div
ref={divEl}
onClick={changeToSelected}
className="p-2"
style={{backgroundColor:"green"}}
>
// do stuff with item heere
</div>
);
}

How to render a stateless functional component from another component

I'm new on React. I wrote a project on which there is a search component. the search works fine ( I checked on console.log) but I don't know how to call the stateless function component on which the search results should be shown?
class SearchCard extends Component {
// qQuery is a variable for query input
state = { qQuery: "" };
HandleSearch= async (e) => {
e.preventDefault();
const {data:cards} = await cardService.getAllCards();
var searchResults = cards.filter((item) =>
item.qTopic.includes(this.state.qQuery) ||
item.qArticle.includes(this.state.qQuery)
);
this.setState({ cards : searchResults });
// console.log('search results ',searchResults, ' cards ',this.state);
return <CardRender cards={cards}/>
}
render() {
return (
<React.Fragment>
<form className="form" onSubmit={ this.HandleSearch }>
<div className="input-group md-form form-sm form-1 pl-4 col-12">
const CardRender = ({cards,favs,onHandleFavs}) => {
return (
<div className="row">
{cards.length > 0 &&
cards.map((card) =>
<Card key={card._id}
card={card}
favs={favs}
onHandleFavs={() => onHandleFavs(card._id)}
/>
}
</div>
);
}
export default CardRender;
screenshot
You should add the <CardRender cards={cards}/> to the element render returns (at the place you want it to be) and render it if state.cards is not empty.
Something like this
class SearchCard extends Component {
// qQuery is a variable for query input
state = { qQuery: "" };
HandleSearch= async (e) => {
// ...
this.setState({ cards : searchResults });
}
render() {
return (
<div>
...
{cards?.length && <CardRender cards={cards}/>}
</div>
);
}
}

How can i create a new element and add onclick attribute to it using dom Manipulation in react

this code works fine in JavaScript but fails to call AddTextBtn function in React.What did i miss?
import React, { Component } from 'react';
class onClickTest extends Component {
AddBtn=()=>{
let btn=document.createElement("button");
let container=document.getElementById("container");
btn.appendChild(document.createTextNode("addTextBtn"));
btn.setAttribute("onClick","{this.addTextBtn}");
container.appendChild(btn);
}
addTextBtn=()=>{
let p=document.createElement("p");
let container=document.getElementById("container");
p.appendChild(document.createTextNode("done"));
container.appendChild(p);
}
render(){
return (
<div id="container">
<button id="AddButton" onClick={this.AddBtn}>Add</button>
</div>
);
}
}
export default onClickTest;
React has its own way of manipulating the DOM so you should never manipulate it yourself.
What you could do is this:
class MyComponent extends Compnent {
state = {
showText: false,
pElements: []
};
handleClick() {
this.setState({ showText: true });
}
hanldeNewP() {
this.setState({ pElements: [...this.state.pElements, <p>Done</p>] });
}
render(){
return (
<div id="container">
<button
id="AddButton"
onClick={this.handleClick}
>
Add
</button>
{ this.state.showText &&
<button onClick={this.handleNewP}>Add P tag</button>
}
{ this.state.pElements }
</div>
);
}
The second button will only be visible if showText is true which happens when the
first button is clicked. Then whenever the second button is clicked a new p element is added in the pElements array which are then rendered using this line: { this.state.pElements }
And if you prefer functional components:
const MyComponent = () => {
const [showText, setShowText] = useState(false);
const [pElements, setPElements] = useState([]);
const handleClick = () => {
setShowText(true);
};
const handleNewP = () => {
setPElements([...pElements, <p>Done</p>]);
}
return (
<div id="container">
<button
id="AddButton"
onClick={handleClick}
>
Add
</button>
{ showText &&
<button onClick={handleNewP}>Add P tag</button>
}
{ pElements }
</div>
);

I change the renderer with one click and then directly query an item (ref). SetTimeout a good solution?

I change the renderer with one click and then directly query an item (ref). setTimeout a good solution?
(I don't know if I change the renderer in a single click, then in the event I can do anything in the fresh renderer. setTimeout good solution? Someone else has a different solution because I feel like I didn't do it well.)
import React from 'react';
class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
con1: false,
con2: false
};
}
handleClick = (e) => {
parseFloat(e.currentTarget.getAttribute('data-id')) === 1 ?
(this.setState({
con1: true,
con2: false
}))
:
(this.setState({
con2: true,
con1: false
}));
/* Good, but this is valid??? */
setTimeout(()=>{
console.log(this.buttonContainer.childNodes[0])
},0)
/* Not good
console.log(this.buttonContainer.childNodes[0]);
*/
}
render() {
const { con1, con2 } = this.state;
return (
<div className="app-container">
<button
data-id="1"
onClick = {(e) => this.handleClick(e)}
>
Button
</button>
<button
data-id="2"
onClick = {(e) => this.handleClick(e)}
>
Button
</button>
<div
className="button-conteiner"
ref={(ref) => this.buttonContainer = ref}
>
{ con1 ?
(<div className="container1">container1</div>)
:
(null)
}
{ con2 ?
(<div className="container2">container2</div>)
:
(null)
}
</div>
</div>
)}
}
export default Test;
I would suggest to use useEffect hook with function components in this case. the logic will be more readable
import React, { useState, useEffect, useRef } from "react";
const Test = props => {
const [con1, setCon1] = useState(false);
const [con2, setCon2] = useState(false);
const buttonContainer = useRef(null);
const handleClick = e => {
const b = parseFloat(e.currentTarget.getAttribute("data-id")) === 1;
setCon1(b);
setCon2(!b);
};
useEffect(() => {
if(buttonContainer.current) {
console.log(buttonContainer.current.childNodes[0])
}
}, [buttonContainer.current, con1, con2])
return (
<div className="app-container">
<button data-id="1" onClick={e => this.handleClick(e)}>
Button
</button>
<button data-id="2" onClick={e => this.handleClick(e)}>
Button
</button>
<div
className="button-conteiner"
ref={buttonContainer}
>
{con1 ? <div className="container1">container1</div> : null}
{con2 ? <div className="container2">container2</div> : null}
</div>
</div>
);
};
export default Test;

Resources