How to access/override the className of an element using refs? - reactjs

I have a text and a button.
What I want to achieve is something like this, that if I clicked the Button the Text will be hidden.
I want to achieve this without using state.
class Test extends Component {
constructor(props){
//codes
}
hide = () => {
const span = this.refs.spanLoading;
span.ClassName = "hidden";
}
render() {
return (
<span ref="spanLoading" id="test-id" className="">The Quick Brown Fox.</span>
<button onClick={() => this.hide()}>Hide</button>
);
}
}
export default Test;

You can use useRef() hook.
Maintain one ref object which is not a string.
const {useRef} = React;
function App() {
const ref = useRef(null);
const onClick = () => {
const span = ref.current; // corresponding DOM node
span.className = "hidden";
};
return (
<div>
<span ref={ref} className="">The Quick Brown Fox.</span>
<button onClick={onClick}>Hide</button>
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById("root")
);
.hidden {
display: none;
}
<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>
<div id="root"></div>

This can be achieved simply, your approach is correct just some few fixes.
You can return only one element in JSX
The ref should be maintained somewhere in JS memory
Check this code and the working codesandbox instance here
class Test extends React.Component {
constructor() {
super();
this.inputRef = React.createRef();
}
hide = () => {
console.log(this.inputRef);
this.inputRef.current.style.visibility="hidden";
};
render() {
return (
<>
<span ref={this.inputRef} id="test-id" className="">
The Quick Brown Fox.
</span>
<button onClick={() => this.hide()}>Hide</button>
</>
);
}
}
EDIT! As you asked about dynamically generated refs...
This is what I understood as your requirement... see whether it matches it.
Working sandbox here
class Test extends React.Component {
constructor() {
super();
this.inputRef = React.createRef();
this.refCollection = {};
for (let id = 0; id < 10; id++) {
this.refCollection["id_" + id] = React.createRef();
}
}
hide = e => {
console.log(this.inputRef);
this.inputRef.current.style.visibility = "hidden";
};
hideInCollection = k => {
let changedRef = this.refCollection[k];
changedRef.current.style.visibility = "hidden";
};
render() {
return (
<>
<span ref={this.inputRef} id="test-id" className="">
The Quick Brown Fox.
</span>
<button onClick={() => this.hide()}>Hide</button>
{Object.keys(this.refCollection).map(k => (
<div>
<span ref={this.refCollection[k]} id="test-id" className="">
Looped the Quick Brown Fox
</span>
<button onClick={() => this.hideInCollection(k)}>Hide</button>
</div>
))}
</>
);
}
}

class Test extends Component {
constructor(props){
this.hide = this.hide.bind(this);
}
hide() {
this.span.classList.add("hidden");
}
render() {
return (
<div>
<span ref={(el) => { this.span = el; }} id="test-id" className="">The Quick Brown Fox.</span>
<button onClick={this.hide}>Hide</button>
</div>
);
}
}
export default Test;
You should return a single element tho

Related

When I press the button I want to add many Employees, but it only leaves me one. React

Good morning, I have a question. When I press the + button, only one employee line is added and I would like it to be added as many times as I press
ReactJS component code:
class Home extends React.Component {
state = { showForm:false }
showForm = () => {
return(
<Employee />
)
}
render() {
return (
<div className='container-home'>
<div className='min-margin'>
<Employee />
{this.state.showForm ? this.showForm() : null}
<div className='container-append'>
<button onClick={() => this.setState({showForm: true})}>➕</button>
</div>
</div>
</div>
)
}
}
You just click to show and hide the input.
You need:
Add to state array: (inputs: ["Employee-0"])
state = {
showForm: false,
inputs: ["Employee-0"]
};
Add to functions
handleAddInput = e => {
e.preventDefault();
const inputState = this.state.inputs;
let inputs = inputState.concat([`Employee-${inputState.length}`]);
this.setState({
inputs
});
};
handleShowForm = e => {
e.preventDefault();
this.setState({
...this.state,
showForm: !this.state.showForm
})
}
Change the code in render
render() {
return (
<div className="App">
{this.state.showForm && <form>
{this.state.inputs.map((input, idx) => (
<Employee key={idx}/>
))}
</form>}
<button onClick={this.handleAddInput}>Add New Employee</button>
<button onClick={this.handleShowForm}>Show form</button>
</div>
);
}
Click on the buttons)
The difference options exist for doing it , but that's work you did just a flag for shown of a Component. So you are able to try followings this:
class Home extends React.Component {
state = {
employeesCount: 0,
employees: []
}
render() {
return (
<div className='container-home'>
<div className='min-margin'>
{employees.map((eNumber) => {
return <Employee key={eNumber}/>
}}
<div className='container-append'>
<button onClick={() => this.setState({
employeesCount: employeesCount + 1,
employees: [...this.state.employess , (employeesCount + 1)]
})}>➕</button>
</div>
</div>
</div>
)
}
}
Try this:
import React from "react";
const Employee = (props) => {
return(
<div>Hello I am employee number {props.number}</div>
)
}
class App extends React.Component {
constructor() {
super()
this.state = { employees: [] }
}
addEmployee() {
this.setState({
employees: [...this.state.employees, <Employee number={this.state.employees.length} />]
})
}
render() {
return (
<div>
<div className='container-append'>
<button onClick={() => this.addEmployee()}>➕</button>
</div>
{ this.state.employees.map(employee => employee) }
</div>
)
}
}
export default App;

Eventhandling in combination with the .map function

Let's say I have a parent component like this:
export default class InputView extends Component {
constructor(props) {
super(props);
this.state = {
nr: [2, 3],
};
}
handleDel = () => {
console.log('klick');
};
render() {
const elements = this.state.nr.map(inputPdf(nr));
return (
<div>
{elements}
</div>
);
}
}
The function inputPdf() creates another component;
const inputPdf = (nr) => {
return (
<div class="card">
<button type="button" class="close" aria-label="Close" onClick={this.props.handleDel()}> </button>
</div>
);
};
And now I want to use the function handleDel() in my child component.
How to get this running...?
There are some issues in code. But if you want to go with your way. It should be like this:
import React from "react";
import "./style.css";
class InputView extends React.Component {
constructor(props) {
super(props);
this.state = {
nr: [2, 3],
};
}
handleDel = () => {
console.log('klick');
};
render() {
const elements = this.state.nr.map((data) => inputPdf(data,this.handleDel));
return (
<div>
{elements}
</div>
);
}
}
const inputPdf = (nr,onClick) => {
return (
<div class="card">
<button type="button" class="close" aria-label="Close" onClick={onClick}>
{nr} </button>
</div>
);
};
export default function App() {
return (
<div>
<InputView/>
<p>Start editing to see some magic happen :)</p>
</div>
);
}
Here is the demo: https://stackblitz.com/edit/react-txqp8k
Issue in code:
Component should have capital name
Instead of rendering {elements} you can directly render component like this inside render
Better way:
return this.state.nr.map(data => <InputPdf onClick={this.handleDel} nr={nr}/>)
//where
const InputPdf = ({nr,onClick}) => {
return (
<div class="card">
<button type="button" class="close" aria-label="Close" onClick={onClick}>
{nr} </button>
</div>
);
}
That's how we can do it.
export default class InputView extends Component {
constructor(props) {
super(props);
this.state = {
nr: [2, 3],
};
}
handleDel = (indexToDelete) => {
console.log("klick");
};
render() {
const elements = this.state.nr.map((elem, index) => {
return <InputPdf item={elem} index={index} handleDel={handleDel} />
});
return <div>{elements}</div>;
}
}
const InputPdf = (props) => {
return (
<div class="card">
<button
type="button"
class="close"
aria-label="Close"
onClick={() => props.handleDel(props.index)}
>
Delete
</button>
</div>
);
};
Let me know if it helps

How to return element in react class functions

How to return element in react class functions on a click. is it even possible?
class Item extends Component {
constructor(props) {
super(props);
this.itemInfo = this.itemInfo.bind(this);
}
itemInfo = () =>{
return <div> some info</div>
}
render(){
return(
<div>
<div onClick={this.itemInfo}> Click Here <div>
</div>
)
}
}
class Item extends React.Component {
state = {
showDiv: false
};
render() {
return (
<div>
<div
style={{ cursor: "pointer" }}
onClick={() =>
this.setState(prevState => ({
showDiv: !prevState.showDiv
}))
}
>
Click Me
</div>
{/*Show the INFO DIV ONLY IF THE REQUIRED STATE IS TRUE*/}
{this.state.showDiv && <InfoDiv />}
</div>
);
}
}
//This is the div which we want on click
var InfoDiv = () => (
<div style={{ border: "2px solid blue",borderRadius:10, padding: 20 }}>
<p> Long Text DIVLong Text DIVLong Text DIVLong Text DIVLong Text DIV </p>
</div>
);
ReactDOM.render(<Item />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
You should do that in the state.
itemInfo = () =>{
this.setState({ component:<div> some info</div> });
}
and render the component like this
return(
<div>
<div onClick={this.itemInfo}> Click Here <div>
{this.state.component}
</div>
)
You can try something like this, using the state and conditional rendering:
class Item extends Component {
constructor(props) {
super(props)
this.state = {
showMore: false,
}
}
toggleShowMore = () => {
this.setState({ showMore: !this.state.showMore })
}
render() {
return (
<div>
<div onClick={this.toggleShowMore}>
{this.state.showMore ? 'Show less' : 'Show more'}
</div>
{this.state.showMore ? <div>some info</div> : null}
</div>
)
}
}
Here's how I would do it:
function ItemInfo() {
return(
<div>Some Info</div>
);
}
class Item extends Component {
constructor(props) {
super(props);
this.handleClick= this.handleClick.bind(this);
this.state = {
showInfo: false
}
}
handleClick() {
this.setState((prevState) => {showInfo: !prevState.showInfo});
}
render(){
return(
<div>
<div onClick={this.handleClick}> Click Here <div>
{ this.state.showInfo ?
<ItemInfo/>
: null }
</div>
)
}
}

Unable to invoke props function passed to children in a loop reactjs

I am new to react. I am just trying to create a comment box and comment board which contain multiple comments.
Each comments have one inputbox, button(save,edit) and button(remove). I have passed function made in board named updateComment to Component Comment as props.
Now When I am trying to execute save of child function in which I have called parent function updateComment using this.props.updateComment
it is giving me error can't read property of undefined.
I have searched for similar question on stackoverflow but I am unable to solved this proplem.
My app.js code is as below.
import React from 'react';
import { Home } from './home.jsx';
class App extends React.Component {
render() {
return (
<div>
<Header/>
<Board />
</div>
);
}
}
class Header extends React.Component {
render() {
return (
<div>
<h1>Header</h1>
</div>
);
}
}
class Board extends React.Component {
constructor(props) {
super(props);
this.state = {
comments:[
"My name is brijesh",
"My name is santosh",
"My name is manoj"
]}
};
removeComment(i) {
console.log("going to remove element i",i);
var arr = this.state.comments;
arr.splice(i,1);
this.setState({comments:arr});
};
updateComment(newComment, i) {
var arr = this.state.comments;
arr[i] = newComment;
this.setState({comments:arr});
};
render() {
return (
<div className="board">
{
this.state.comments.map(function(text,i) {
return (
<Comment key ={i} index = {i}
updateComment={() => {this.updateComment}}
removeComment={() => {this.removeComment}}>
{text}
</Comment>
)
})
}
</div>
)
}
}
class Comment extends React.Component {
constructor(props) {
super(props);
this.state = {
edit: false
};
};
edit(){
this.setState({edit:true});
console.log("you clickced on edit0");
};
save(){
this.setState({edit:false});
var newText = this.refs.newText.value;
this.props.updateComment(newText, this.props.index);
console.log("you clickced on edit0",newText);
};
handleChange(event) {
this.setState({value: event.target.value});
}
render() {
if(this.state.edit) {
return (
<div>
<div className="comment">
<input type="text" ref="newText" defaultValue={this.props.children} onChange={ this.handleChange.bind(this) } />
<button onClick={this.save.bind(this)}>Save</button>
</div>
</div>
)
}
else {
return (
<div>
<div className="comment">
<div>{ this.props.children }</div>
<button onClick={this.edit.bind(this)}>Edit</button>
</div>
</div>
)
}
}
}
export default App
And my main.js looks like this.
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.jsx';
ReactDOM.render(
( < App / > ), document.getElementById('app'));
I have also created fiddle also.
https://jsfiddle.net/aubrijesh/k3h2pcnj/#&togetherjs=uEI7TFnJD1
I believe that DOMZE is on the right track but you should also bind the function in the map statement. In my opinion arrow functions makes it much easier to keep track of what this refers to.
class Board extends React.Component {
constructor(props) {
super(props);
this.state = {
comments:[
"My name is brijesh",
"My name is santosh",
"My name is manoj"
]}
};
removeCommment(i) {
console.log("going to remove element i",i);
var arr = this.state.comments;
arr.splice(i,1);
this.setState({comments:arr});
};
updateComment(newComment, i) {
var arr = this.state.comments;
console.log("new Comment");
arr[i] = newComment;
this.setState({comments:arr});
};
render() {
return (
<div className="board">
{
this.state.comments.map((text,i) => {
return (
<Comment key ={i} index = {i}
updateComment={() => {this.updateComment}}
removeComment={() => {this.removeComment}}>
{text}
</Comment>
)
})
}
</div>
)
}
}
class Comment extends React.Component {
constructor(props) {
super(props);
this.state = {
edit: false
};
};
edit(){
this.setState({edit:true});
console.log("you clickced on edit0");
};
save(){
this.setState({edit:false});
var newText = this.refs.newText.value;
this.props.updateComment(newText, this.props.index);
console.log("you clickced on edit0",newText);
};
handleChange(event) {
this.setState({value: event.target.value});
}
render() {
if(this.state.edit) {
return (
<div>
<div className="comment">
<input type="text" ref="newText" defaultValue={this.props.children} onChange={ this.handleChange} />
<button onClick={this.save.bind(this)}>Save</button>
</div>
</div>
)
}
else {
return (
<div>
<div className="comment">
<div>{ this.props.children }</div>
<button onClick={this.edit.bind(this)}>Edit</button>
</div>
</div>
)
}
}
}
ReactDOM.render(<Board />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
update your render method
let self = this;
return (
<div className="board">
{
self.state.comments.map(function(text,i) {
return (
<Comment key ={i} index = {i}
updateComment={() => {self.updateComment}}
removeComment={() => {self.removeComment}}>
{text}
</Comment>
)
})
}
</div>
)
You need to bind the class to the function, so that it knows what "this" is
render() {
return (
<div className="board">
{
this.state.comments.map(function(text,i) {
return (
<Comment key ={i} index = {i}
updateComment={this.updateComment.bind(this)}
removeComment={this.removeComment.bind(this)}>
{text}
</Comment>
)
})
}
</div>
)
}
Note that you may want to do those bindings in the constructor so that it doesn't bind at each and every render

onClick in reactjs not working

Below is my code. My onClick is nor working. It always through error "Uncaught TypeError: Cannot read property 'likeQuestion' of undefined". But my "gotoPage" function is working. I don't know where I am wrong. I am very new in Reactjs. Why "likeQuestion" function is not recognized.
My first onClick is working
export default class Question extends React.Component {
constructor(){
super();
this.toggle = this.toggle.bind(this);
this.state = {
pageNo : 1,
dropdownOpen: false,
questioninfo : []
}
}
componentWillMount(){
//some action
}
gotoPage(index) {
//some action. This is working
}
toggle() {
this.setState({
dropdownOpen: !this.state.dropdownOpen
});
}
likeQuestion(e){
console.log('this is clicked');
//But this is not working
}
render() {
var canvases = this.state.questionItem.map(function(data,i) {
var firstLtr = data.user_name.charAt(0);
return (
<div key={i}>
<Col sm="12" md={{ size: 12, offset: 2 }} className="questionCard">
<Card block>
<CardTitle>
<div className="outerCircle"><span>{firstLtr}</span></div> {data.user_name}
<i className="fa fa-flag-o flagging" aria-hidden="true"></i>
{data.location_url}
</CardTitle>
<CardText className="questionTxt">{data.message}</CardText>
<div>
<Button className="replyBtn" disabled>No Discussion</Button>
<Button size="sm" color="link" className="disussionSpan" onClick={(i) => this.likeQuestion(i)}>{data.likes} Likes</Button>
</div>
</Card>
</Col>
</div>
);
});
return(
<div className="container">
<div className="row">
<div className="pageInfo">
<Dropdown className="inline" isOpen={this.state.dropdownOpen} toggle={this.toggle}>
<DropdownToggle caret>
Pages
</DropdownToggle>
<DropdownMenu>
{pgrow}
</DropdownMenu>
</Dropdown>
<p className="inline currPgNo">Page: {currentPage}</p>
</div>
<div className="col-md-8 col-md-offset-2">
{canvases}
</div>
</div>
</div>
)
}
React wouldn't auto-bind map inside render(), so you have to do it yourself in order to use this and call this.likeQuestion. Luckily, map provides a second argument to specify the context (this).
So just use...
this.state.questionItem.map(function(data,i) {
...
}, this)
instead of
this.state.questionItem.map(function(data,i) {
...
})
Option 2: Use arrow function in the map, such as map((data, i) => ...
Option 3: bind this to likeQuestion in the constructor of the component.
Try to define your helper functions using arrow functions
gotoPage = (index) => {
//some action. This is working
}
toggle = () => {
this.setState({
dropdownOpen: !this.state.dropdownOpen
});
}
likeQuestion = (e) => {
console.log('this is clicked');
//But this is not working
}
or
Bind these methods in constructor of your React component. e.g
this.likeQuestion = this.likeQuestion.bind(this);
// Needs to be done for all the helper methods.
So that you access the class level this context.
E.g a minimal setup
class Question extends React.Component {
constructor(props) {
super(props);
this.state = {
likes:10
};
}
likeQuestion = (e) => {
console.log('this is clicked');
//But this is not working
}
render() {
return ( < div >
< button size = "sm"
color = "link"
className = "disussionSpan"
onClick = {
(i) => this.likeQuestion(i)
} > {
this.state.likes
}
Likes < /button>
< /div >
);
}
};
ReactDOM.render( < Question / > , document.querySelector('#test'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="test">
</div>

Resources