Highlight item onClick - React.js - reactjs

Add underscore to category-item onClick and remove underscore for any other item. Found some answers on how to do this with only two components, a "item-container-component" and "item-components". But i have three components involved. This is what I hope to achieve:
Archive-component (mother component):
class Archive extends React.Component {
constructor(props){
super(props);
this.state = {
products: [],
category: "",
activeIndex: 0
}
this.filterHandler = this.filterHandler.bind(this);
}
filterHandler(tag, index){
console.log('INDEX: ' + index);
this.setState({
category: tag,
activeIndex: index
})
}
componentDidMount(){
const myInit = {
method: "GET",
headers: {
"Content-Type": "application/json"
}
};
fetch("/getProducts", myInit)
.then((res) => {
return res.json();
})
.then((data) => {
this.setState({products:data});
})
.catch(function(err) {
console.log('ERROR!!! ' + err.message);
});
}
render() {
return (
<div>
<Menu />
<div className="archive-container">
<div className="archive-wrapper">
<CategoryContainer
filterHandler={this.filterHandler}
products={this.state.products}
activeIndex={this.state.activeIndex}
/>
<br/><br/>
<ProductContainer
products={this.state.category.length
? this.state.products.filter((prod) => prod.category === this.state.category)
: this.state.products.filter((prod) => prod.category === 'Paint')
}
/>
</div>
</div>
<Footer />
</div>
);
};
};
Category-container component:
class CategoryContainer extends Component {
render(){
const categories = [...new Set(this.props.products.map(cat => cat.category))];
return (
<div>
<ul className="filterList">{
categories.map((cat, index) =>
<CategoryItem
key={index}
index={index}
category={cat}
active={index === this.props.activeIndex}
handleClick={() => this.props.filterHandler(cat, index)}
/>
)
}</ul>
</div>
);
};
};
Category-item component:
class CategoryItem extends Component {
render(){
return (
<div>
<li
className={this.props.active ? 'active' : 'category'}
onClick={this.props.handleClick}
>
{this.props.category}
</li>
</div>
);
};
};
Yelp!
M

Suppose you have a li tag for which you want to change the color of.
you could probably try something like this.
<li id="colorChangeOnClick" class="redColor" onclick={this.onClickFunction()}></li>
Then in your react class you can have the on click function with parameters e:
onClick(e) {
//This would give you all the field of the target
console.log(e.target.elements);
// you can do all sorts of Css change by this way
e.target.element.class="newGreenColor";
}
Also make sure to make a state or a prop change otherwise the page would not render again.

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;

React setstate does not work in IE11 after render is called

I am new to react development and I have a react app where on the componentDidMount am setting the state of value as "add" and it renders the div content for "add" and once button click on the add div am calling an addstate method
where am setting the state of the value as "edit" and it renders the div content with respect to "edit" and where i call again the addstate method through done method call.
In this case the fetch call from addstate method is happening to the backend but the state is not setting back to edit..it fails only in IE11. It works on chrome, firefox and mobile devices.
If i remove the piece of code "Value:edit" in addstate method its working good. But my requirement needs to render based upon different scenarios. so basically am able to set the state of the result only once in IE11. it does not work repeatedly.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: "test",
items: []
}
};
addState() {
fetch("/local/addThings").then(res => res.json())
.then(
(result) => {
this.setState({
value: "edit",
items: result
});
}
)
.catch(error => console.error('Error:', error));
}
done() {
this.setState({
value: "add"
});
}
componentDidMount() {
fetch("/local/getThings")
.then(
(result) => {
this.setState({
value: "add"
});
}
)
.catch(error => console.error('Error:', error));
}
render() {
const { value, items } = this.state;
if (value === "add") {
return <div >
<div >
<ul >
<li onClick={() => this.addState()}>
<div>
<img src="Add.png" />
<center><p>AddButton</p></center>
</div>
</li>
</ul>
</div>
</div>
;
}
if (value === "edit") {
return (<div>
<div >
<ul >
<li onClick={() => this.done()}>
<div >
<img src="Save.png" />
<center><p>SaveButton</p></center>
</div>
</li>
{items.map(item => (
<center><p>{item.name}</p></center>
</li>
))}
</ul>
</div>
</div>
);
}
}
}
ReactDOM.render(React.createElement(App, null), document.getElementById("details")); ```
Try binding your method and refer to it directly in your onClick event:
lass App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: "test",
items: []
}
this.addState = this.addState.bind(this);
};
addState() {
fetch("/local/addThings").then(res => res.json())
.then(
(result) => {
this.setState({
value: "edit",
items: result
});
}
)
.catch(error => console.error('Error:', error));
}
done() {
this.setState({
value: "add"
});
}
componentDidMount() {
fetch("/local/getThings")
.then(
(result) => {
this.setState({
value: "add"
});
}
)
.catch(error => console.error('Error:', error));
}
render() {
const { value, items } = this.state;
if (value === "add") {
return <div >
<div >
<ul >
<li onClick={this.addState}>
<div>
<img src="Add.png" />
<center><p>AddButton</p></center>
</div>
</li>
</ul>
</div>
</div>
;
}
if (value === "edit") {
return (<div>
<div >
<ul >
<li onClick={() => this.done()}>
<div >
<img src="Save.png" />
<center><p>SaveButton</p></center>
</div>
</li>
{items.map(item => (
<center><p>{item.name}</p></center>
</li>
))}
</ul>
</div>
</div>
);
}
}
}
I added settimeout in my methods and it worked in IE. It seems like the response was slower in IE for the API calls. Not sure if there is a workaround or this is the right approach.
fetch("/local/addThings").then(res => res.json())
.then(
(result) => {
setTimeout(() => {
this.setState({
value: "edit",
items: result
});
}, 200);
}
)
.catch(error => console.error('Error:', error));
}```

Cannot change the state of parent component and re-render

im new to React, trying to make some simple 'Chat' app, stuck a bit in some feature.
im trying to make user list, that onClick (on one of the user) it will change the class (to active), and when hitting another user it will set the active class to the new user.
tried a lot of things, managed to make it active, but when hitting another user, the old one & the one receive the 'active' class.
here is my Parent componenet
class Conversations extends React.Component {
constructor(props) {
super(props);
this.loadConversations = this.loadConversations.bind(this);
this.selectChat = this.selectChat.bind(this);
this.state = { count: 0, selected: false, users: [] }
}
selectChat = (token) => {
this.setState({ selected: token });
}
loadConversations = (e) => {
$.get('/inbox/get_conversations', (data) => {
let r = j_response(data);
if (r) {
this.setState({ count: r['count'], users: r['data']});
}
});
}
componentDidMount = () => {
this.loadConversations();
}
render() {
return (
<div>
{this.state.users.map((user) => {
return(<User selectChat={this.selectChat} selected={this.state.selected} key={user.id} {...user} />)
})}
</div>
)
}
here is my Child componenet
class User extends React.Component {
constructor(props) {
super(props);
this.handleSelect = this.handleSelect.bind(this);
this.state = {
token: this.props.token,
selected: this.props.selected,
username: this.props.username
}
}
handleSelect = (e) => {
//this.setState({selected: e.target.dataset.token});
this.props.selectChat(e.target.dataset.token);
}
render() {
return (
<div data-selected={this.props.selected} className={'item p-2 d-flex open-chat ' + (this.props.selected == this.props.token ? 'active' : '')} data-token={this.props.token} onClick={(e) => this.handleSelect(e)}>
<div className="status">
<div className="online" data-toggle="tooltip" data-placement="right" title="Online"></div>
</div>
<div className="username ml-3">
{this.props.username}
</div>
<div className="menu ml-auto">
<i className="mdi mdi-dots-horizontal"></i>
</div>
</div>
)
}
Any help will be great...hope you can explain me why my method didnt work properly.
Thank you.
You can make use of index from map function to make element active.
Initially set selected to 0;
this.state = { count: 0, selected: 0, users: [] }
Then pass index to child component,also make sure you render your User component when you are ready with data by adding a condition.
{this.state.users.length > 0 && this.state.users.map((user,index) => {
return(<User selectChat={this.selectChat} selected={this.state.selected} key={user.id} {...user} index={index} />)
})}
In child component,
<div data-selected={this.props.selected} className={`item p-2 d-flex open-chat ${(this.props.selected === this.props.index ? 'active' : '')}`} data-token={this.props.token} onClick={() => this.handleSelect(this.props.index)}>
...
</div>
handleSelect = (ind) =>{
this.props.selectChat(ind);
}
Simplified Demo using List.

Passing react function to children down, event.target empty

I'm struggling to grasp a react concept that to me is likely used all the time.
I have an app with a state.
I have a section below app.
Below section I have clickable tile that receives a function to update app status. This works, however the event.target appears to be null.
I'm passing the function to update the status all the way down from app as a prop.
How can I fix this / what am I missing?
import React, { Component } from 'react';
import './App.css';
const Section = ({ handleClick }) => {
return (
<div className="section">
Section
<Tile handleClick={handleClick} title="1" />
<Tile handleClick={handleClick} title="2" />
<Tile handleClick={handleClick} title="3" />
</div>
)
}
const Tile = ({ handleClick, title }) => {
return (
<div className="tile" onClick={handleClick}>
tile {title}
</div>
)
};
class App extends Component {
constructor(props) {
super(props);
this.state = {
modalOpen: false
};
}
openModal = () => {
this.setState({
modalOpen: true,
openedBy: ""
})
}
closeModal = (event) => {
this.setState({
modalOpen: false,
openedBy: event.target.title
})
}
render() {
return (
<div className="App">
<div>ModalOpen = {this.state.modalOpen.toString()}</div>
<div>Opened by = {this.state.openedBy}</div>
<Section handleClick={this.openModal}></Section>
<a href="#" onClick={this.closeModal}>Close modal</a>
</div>
);
}
}
export default App;
Thanks so much for pointer in the right direction!
You are not passing down a title prop to your Tile component, but your are passing down a number prop.
You can create a new function in the Tile component that calls the handleClick with the number, which you then use to set the openedBy in your App.
Example
const Section = ({ handleClick }) => {
return (
<div className="section">
Section
<Tile handleClick={handleClick} number="1" />
<Tile handleClick={handleClick} number="2" />
<Tile handleClick={handleClick} number="3" />
</div>
);
};
const Tile = ({ handleClick, number }) => {
return (
<div className="tile" onClick={() => handleClick(number)}>
tile {number}
</div>
);
};
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
modalOpen: false,
openedBy: ""
};
}
openModal = title => {
this.setState({
modalOpen: true,
openedBy: title
});
};
closeModal = () => {
this.setState({
modalOpen: false,
openedBy: ""
});
};
render() {
return (
<div className="App">
<div>ModalOpen = {this.state.modalOpen.toString()}</div>
<div>Opened by = {this.state.openedBy}</div>
<Section handleClick={this.openModal} />
<a href="#" onClick={this.closeModal}>
Close modal
</a>
</div>
);
}
}
ReactDOM.render(<App />, 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>
It seems to be working perfectly fine :
const Section = ({ handleClick }) => {
return (
<div className="section">
Section
<Tile handleClick={handleClick} number="1" />
<Tile handleClick={handleClick} number="2" />
<Tile handleClick={handleClick} number="3" />
</div>
)
}
const Tile = ({ handleClick, title }) => {
return (
<div className="tile" onClick={handleClick}>
tile {title}
</div>
)
};
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
modalOpen: false
};
}
openModal = event => {
console.log(event.target)
this.setState({
modalOpen: true,
openedBy: ""
})
}
closeModal = event => {
console.log(event.target)
this.setState({
modalOpen: false,
openedBy: event.target.title
})
}
render() {
return (
<div className="App">
<div>ModalOpen = {this.state.modalOpen.toString()}</div>
<div>Opened by = {this.state.openedBy}</div>
<Section handleClick={this.openModal}></Section>
<a href="#" onClick={this.closeModal}>Close modal</a>
</div>
);
}
}
ReactDOM.render(<App/>, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.5.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.5.2/umd/react-dom.production.min.js"></script>
<div id='root'>
However I assume that you want to pass down the title of the clicked element back into the handler. If so, I recommend using a curried function, with 2 sets of parameters, and setting the title variable as the first one :
openModal = title => event => {
console.log('Opened by : ', title, event.target)
this.setState({
modalOpen: true,
openedBy: ""
})
}
Your Tile component can now indicate which title it has by calling the function the first time :
const Tile = ({ handleClick, title }) => {
return (
<div className="tile" onClick={handleClick(title)}>
tile {title}
</div>
)
};
Working example :
const Section = ({ handleClick }) => {
return (
<div className="section">
Section
<Tile handleClick={handleClick} title="1" />
<Tile handleClick={handleClick} title="2" />
<Tile handleClick={handleClick} title="3" />
</div>
)
}
const Tile = ({ handleClick, title }) => {
return (
<div className="tile" onClick={handleClick(title)}>
tile {title}
</div>
)
};
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
modalOpen: false
};
}
openModal = title => event => {
console.log('Opened by : ', title)
this.setState({
modalOpen: true,
openedBy: ""
})
}
closeModal = event => {
this.setState({
modalOpen: false,
openedBy: event.target.title
})
}
render() {
return (
<div className="App">
<div>ModalOpen = {this.state.modalOpen.toString()}</div>
<div>Opened by = {this.state.openedBy}</div>
<Section handleClick={this.openModal}></Section>
<a href="#" onClick={this.closeModal}>Close modal</a>
</div>
);
}
}
ReactDOM.render(<App/>, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.5.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.5.2/umd/react-dom.production.min.js"></script>
<div id='root'>

React - Map content inside a div

Good Morning! Why does my map content stay outside the "blog--div" div?
It's getting loose on Body and I do not know why. Help-me, please!
I try to put a border around the contents of the "blog--div" but the content becomes loose, making it impossible to apply styles.
imports[...]
class Blog extends Component {
constructor(props) {
super(props)
this.state = {
post: [],
}
}
componentDidMount() {
this.setState({ isLoading: true })
fetch(`${API}`)
.then(res => res.json())
.then(res => {
this.setState({
post: [res],
isLoading: false,
})
})
}
render() {
const { isLoading } = this.state
if (isLoading) {
return <Loading />
}
return (
<div className="blog">
<p className="blog__title">Blog</p>
{this.renderBlog()}
</div>
)
}
renderBlog() {
const page = this.state.post.map((post, key) => {
return (
<div className="blog--div" key={key}>
<div className="blog__post">
<div className="blog__post--title">
<p><a target="_blank" rel="noopener noreferrer" href={post[0].link}>{post[0].title.rendered.replace('Visit.Rio', 'Projeto 1')}</a></p>
</div>
<div className="blog__post--description">
<p>{post[0].excerpt.rendered.replace('Visit.Rio', 'Projeto 1')}</p>
</div>
</div>
</div>
)
})
return page
}
}
export default Blog

Resources