Handle multiple child component in React - reactjs

I've tried to look everywhere and couldn't find anything related to my use case, probably I'm looking for the wrong terms.
I have a situation where I have a bar with 3 icons, I'm looking for set one icon "active" by changing the class of it.
The icon is a custom component which have the following code
export default class Icon extends Component {
state = {
selected : false,
}
setSelected = () => {
this.setState({
selected : true
})
}
setUnselected = () => {
this.setState({
selected : false
})
}
render() {
var classStatus = '';
if(this.state.selected)
classStatus = "selected-icon"
else
classStatus = "icon"
return <div className={classStatus} onClick={this.props.onClick}><FontAwesomeIcon icon={this.props.icon} /></div>
}
}
In my parent component I have the following code
export default class MainPage extends Component {
handleClick(element) {
console.log(element);
alert("Hello!");
}
render() {
return (
<div className="wrapper">
<div className="page-header">
<span className="menu-voice">File</span>
<span className="menu-voice">Modifica</span>
<span className="menu-voice">Selezione</span>
</div>
<div className="page-main">
<span className="icon-section">
<div className="top-icon">
<Icon icon={faFileCode} onClick={() => this.handleClick(this)} />
<Icon icon={faCodeBranch} onClick={() => this.handleClick(this)} />
<Icon icon={faMagnifyingGlass} onClick={() => this.handleClick(this)} />
</div>
</span>
<span className="files-section">Files</span>
<span className="editor-section"></span>
</div>
<div className="page-footer">
Footer
</div>
</div>
);
}
}
What I'm trying to achieve is that when one of the Icon child component get clicked it will set the selected state to true manage by the parent component, in the same time while one of them is true I would like that the parent would set to false the other twos.
I've tried to use the useRef function but it doesn't look as a best practise.
Which is the correct way to do it? Sending also this to the handleClick function it just return the MainPage class instead of the child. Any suggestion at least where I should watch?
Thanks in advance

I suggest not storing the state in the icon, since it doesn't know what else you're using it for. Simply have the icon component take it's 'selected' status from props. e.g.
export default class Icon extends Component {
render() {
var classStatus = '';
if(this.props.selected)
classStatus = "selected-icon"
else
classStatus = "icon"
return (
<div className={classStatus} onClick={this.props.onClick}>.
<FontAwesomeIcon icon={this.props.icon} />
</div>
);
}
};
Then you can just manage the state in the parent where it should be:
export default class MainPage extends Component {
constructor(props) {
super(props);
this.state = { selectedOption : '' };
}
handleSelectOption(newValue) {
this.setState({ selectedOption: newValue });
}
isSelected(value) {
return value === this.state.selectedOption;
}
render() {
return (
<div className="wrapper">
{ /* etc... */ }
<div className="page-main">
<span className="icon-section">
<div className="top-icon">
<Icon
icon={faFileCode}
onClick={() => this.handleSelectOption("File")}
selected={isSelected("File")}
/>
<Icon
icon={faCodeBranch}
onClick={() => this.handleSelectOption("Modifica")}
selected={isSelected("Modifica")}
/>
{ /* etc... */ }
</div>
</span>
</div>
{ /* etc... */ }
</div>
);
}
};

You should define a constructor in your class component:
constructor(props) {
super(props);
this.state = { selected : false };
}
You also have to call a function which modify the state when you click on the Icon. onClick={this.props.onClick} doesn't change the state

Related

Why is my class component not rendering correctly?

I've been at this awhile and I can't get my head around it. I feel so stupid. Can anyone tell me what's wrong?
The console log works currently, and the console.log of the Object states that the state has been updated from false to true.
class ChoiceBar extends React.Component {
constructor() {
super();
this.state = {
handleFirstQuestion: false,
};
this.handleFirstQuestion = this.handleFirstQuestion.bind(this)
}
handleFirstQuestion() {
console.log("Start Rendering First Question")
this.setState({handleFirstQuestion: true}, () => {console.log(this.state);
});
}
render() {
return (
<div>
<center>
<div>
<button onClick={this.handleFirstQuestion.bind(this)}> Start! </button>
<p> </p>
{this.state.handleFirstQuestion
? <FirstQuestionBox />
: null
}
</div>
</center>
</div>
)
}
}
The first thing is that you are binding the function twice, one in constructor and one in onClick event handler.
Second pass props component in constructor and super
constructor(props) {
super(props);
this.state = {
handleFirstQuestion: false,
};
this.handleFirstQuestion = this.handleFirstQuestion.bind(this)
}
return (
<div>
<center>
<div>
<button onClick={this.handleFirstQuestion}> Start! </button>
<p> </p>
{this.state.handleFirstQuestion
? <FirstQuestionBox />
: null
}
</div>
</center>
</div>
)
I don't think you are exporting the class component
write this after the class component
export default ChoiceBar;
you should probably use aero functions and useState hook instead of the class component
for example:
const ChoiceBar = () => {
const [handleFirstQuestion, setHandleFirstQuestion] = useState(false)
}

Add class to following buttons inside a map function

I have a function that adds class onClick.
import * as React from 'react'
class ThisClass extends React.Component<any,any> {
constructor(){
super()
this.state = {active: ''}
this.ThisFunction = this.ThisFunction.bind(this)
}
const items = ['button1', 'button2', 'button3']
ThisFunction (i) {
this.setState({
active: i
})
}
render(){
return(
<div>
{
items.map((item, i) => {
return(
<button
onClick={()=>this.ThisFunction(i)}>{item}
className={`this_button ${this.state.active ? 'active' : ''}`}
{item}
</button>
)
})
}
</div>
)
}
}
export default ThisClass
What's suppose to happen:
<div>
<button class='this_button active'>button1</button>
<button class='this_button active'>button2</button>
<button class='this_button'>button3</button>
</div>
What's really happening:
<div>
<button class='this_button active'>button1</button>
<button class='this_button'>button2</button>
<button class='this_button'>button3</button>
</div>
I need to have a maximum of two active' class onClick function. Is there any way to get around this?
Change your state to hold an array of active indices and change the handler to only maintain the last two set (i.e. kick out the least recently used)
state = {
active: []
};
thisFunction = i => {
if (this.state.active.includes(i)) return; // already active!!
const active = [...this.state.active, i].slice(-2); // keep last 2
this.setState({ active });
};
You also had some syntax errors in the button rendering. The classname was being set as part of the button text. Set the active class if this.state.active array includes the active index
{["button1", "button2", "button3"].map((item, i) => {
return (
<button
onClick={() => this.thisFunction(i)}
className={`this_button ${
this.state.active.includes(i) ? "active" : ""
}`}
>
{item}
</button>
);
})}
There are some major errors in your code.
You are putting the className property as a child on the button, is an attribute instead
Every child on a list e.g your buttons needs to have a key
You are binding twice the function, one on the constructor and another one on the onClick event using an arrow function. Bind a function
class ThisClass extends React.Component {
constructor(){
super()
this.state = {active: ''}
}
ThisFunction (i) {
this.setState({
active: i
})
}
render(){
const items = ['button1', 'button2', 'button3']
return(
<div>
{
items.map((item, i) => {
return(
<button
key={i}
onClick={() => this.ThisFunction(i)}
className={`this_button ${this.state.active === i ? 'active' : ''}`}>
{item}
</button>
)
})
}
</div>
)
}
}
ReactDOM.render(
<ThisClass />,
document.body
);
<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>

Reactjs - how to append a font-awesome icon to ref

I'm trying to use appendChild to add a fontawesome icon to a ref in react. I get the error:
Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'.
Using append instead of appendChild can only add text.
class Example extends React.Component {
handle = (e) => {
e.target.appendChild('<i className="fas fa-minus-circle"/>')
}
render() {
return (
<div className="container">
<button onClick={this.handle}>CLICK HERE</button>
</div>
)
}
}
Consider changing your approach a little. You can achieve the same result you're expecting with the code below, and it's a little more common to use state and conditional JSX in React:
class Example extends React.Component {
state = { images: [] }
handle = () => {
this.setState({ images: [...images, 'fas fa-minus-circle'] });
}
render() {
return (
<div className="container">
<button onClick={this.handle}>CLICK HERE</button>
{this.state.images.map((image, index) => {
<i key={index} className={image} />
})}
</div>
);
}
}
You can create ref and attach it to the element you want to add the icon.
class Example extends React.Component{
constructor(props){
super(props);
this.iconRef= React.createRef();
}
handle = (e) => {
e.prenventDefault();
this.iconRef.current.appendChild('<i className="fas fa-minus-circle"/>')
}
render() {
return (
<div className="container">
<button ref={this.iconRef} onClick={this.handle}>CLICK HERE</button>
</div>
)
}
}

How to remove an element in reactjs by an attribute?

I want to get unknown key attribute using known ID so that i may delete corresponding div.
I tried using document.getElementById("a").getAttribute('key'); , but it isn't working. May be my concept is wrong.
class PostAdded extends Component {
constructor(props) {
super();
this.deletepost = this.deletepost.bind(this);
}
deletepost() {
let ab =document.getElementById("a").getAttribute('key');
console.log(ab)
}
render() {
return (
<div>
{ this.props.posts.map((post, i) =>
<div id="a" key={`i-${post.title}`}>
<span> <h3>{post.title}</h3><p>{post.post}</p></span>
<input type="button" value="Delete" onClick={this.deletepost}/>
</div>
) }
</div>
)
}
}
export default PostAdded;
If you were able to delete the div, that probably wouldn't end up working for you anyway because any state change would cause a re-render and it would appear again. Instead, you could keep track of your posts in state and then remove one of the posts from state in your deletepost method.
class PostAdded extends Component {
constructor(props) {
super();
this.state = {
posts: props.posts
}
this.deletepost = this.deletepost.bind(this);
}
deletepost(index) {
const newPosts = this.state.posts.splice(index)
this.setState({posts: newPosts})
}
render() {
return (
<div>
{ this.state.posts.map((post, i) =>
<div id="a" key={`i-${post.title}`}>
<span> <h3>{post.title}</h3><p>{post.post}</p></span>
<input type="button" value="Delete" onClick={() => this.deletepost(i)}/>
</div>
) }
</div>
)
}
}
export default PostAdded;

react change the class of list item on click

I have a react element like this:
import React, { PropTypes, Component } from 'react'
class AlbumList extends Component {
constructor(props) {
super(props);
this.state = {'active': false, 'class': 'album'};
}
handleClick() {
if(this.state.active){
this.setState({'active': false,'class': 'album'})
}else{
this.setState({'active': true,'class': 'active'})
}
}
render() {
var album_list
const {user} = this.props
if(user.data){
list = user.data.filter(album => album.photos).map((album => {
return <div className={"col-sm-3"} key={album.id}>
<div className={this.state.class} key={album.id} onClick={this.handleClick.bind(this)}>
<div className={"panel-heading"}>{ album.name }</div>
<div className={"panel-body"}>
<img className={"img-responsive"} src={album.photo.source} />
</div>
</div>
</div>
}))
}
return (
<div className={"container"}>
<div className="row">
{list}
</div>
</div>
)
}
}
export default AlbumList
Here map gives the list of filter data as I wanted. Here what I am doing changes the class of all the list element if I click on one.
I am getting the class name from this.state.class
How can I change the class of only element that i have clicked..
Thanks in advance ...
I have considered it once.So you have so many divs and you want to know which is clicked.My way to solve this problem is to give a param to the function handleClick and you can get the dom of the div while you click the div.Like this:
array.map(function(album,index){
return <div onClick={this.handleClick}/>
})
handleClick(e){
console.log(e.target);
e.target.className = 'active';
...
}
Then you have a param for this function.While you can use the e.target to get the dom of your div which is clicked.
There are some mistake into your code about the state.class.
class AlbumList extends Component {
constructor(props) {
super(props);
this.state = {'active': false, 'class': 'album'};
}
handleClick(e) {
if(e.target.class === 'active'){
e.target.className = 'album'
}else{
e.target.className = 'active'
}
}
render() {
var album_list
const {user} = this.props
if(user.data){
list = user.data.filter(album => album.photos).map((album => {
return (
<div className={"col-sm-3"} key={album.id}>
<div className='active' key={album.id} onClick={this.handleClick.bind(this)}>
<div className={"panel-heading"}>{ album.name }</div>
<div className={"panel-body"}>
<img className={"img-responsive"} src={album.photo.source} />
</div>
</div>
</div>
)
}))
}
return (
<div className={"container"}>
<div className="row">
{list}
</div>
</div>
)
}
}
You can try this and tell me anything wrong.

Resources