Get position of 3 seperate divs in ReactJS - reactjs

I have 3 divs, which I'm displaying in a ReactJS app.
I'm displaying the divs, by looping through an object of classNames stored in state. (Each className has it's own CSS styling, which displays a color - hat1, hat2, hat3).
OnClick, I want to get the div coordinates/position of any of the 3 divs I click on.
I've tried using React.createRef() and getBoundingClientRect(). However, both methods give me the same coordinates, no matter which div I click on.
It looks like it's returning the coordinates of the <section> tag, rather than the target div element I click on...
What am I doing wrong?
class Cylinders extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
this.state = {
divs: [
{
className: 'hat1'
},
{
className: 'hat2'
},
{
className: 'hat3'
}
]
}
}
componentDidMount = () => {
console.log('mount');
}
handleClick = (item, i) => {
console.log('item', item);
console.log('i', i);
var divCoordinates = ReactDOM.findDOMNode(this).getBoundingClientRect();
console.log(divCoordinates, 'divCoordinates');
// const node = this.myRef.current;
// console.log('node', node);
}
render() {
return (
<section>
<div className="columns is-mobile">
<div className="column">
<h1 className="title has-text-black is-size-2">Cylinders Game</h1>
<button className="has-text-black">Ball container</button>
</div>
</div>
<div className="columns is-mobile">
<div className="colum ballContainer">
<div className="ball"></div>
</div>
</div>
<div className="columns is-mobile">
{this.state.divs.map((item, i) => {
return (
<div className="column">
<div className="columns is-multiline">
<div
onClick={() => this.handleClick(item, i)}
className={item.className}
key={item.name + i}
ref={el => this.containerLine = el}
> {i}
</div>
</div>
</div>
)
})}
</div>
</section>
);
}
}
export default Cylinders;

I got it working!
I simply targeted the item, with item.target.
var divCoordinates = ReactDOM.findDOMNode(item.target).getBoundingClientRect();

Related

Force update to make functional component re-render

I'm doing pokedex (pokemon wiki stuff). I want to change my component view, when clicking on pokemon images (description lookalike). When I click on an image - nothing happens (firstly, I want at least pokemon's name to be added to the pokemonDescription array). What am I doing wrong?
let pokemonDescription = [];
const useForceUpdate = () => {
const [value, setValue] = useState(true);
return () => setValue(value => !value);
}
const forceUpdate = useForceUpdate();
const onPokemonClick = (event) => {
console.log(
"wrapper clicked, event.target - ",
event.target.getAttribute('data-name')
);
pokemonDescription = [];
pokemonDescription.push(event.target.getAttribute('data-name'));
console.log("description array -", pokemonDescription);
forceUpdate();
};
useEffect(() => {
document.querySelector(".wrapper").addEventListener("click", onPokemonClick);
...
return () => {
document.querySelector(".wrapper").removeEventListener("click", onPokemonClick);
};
}, []);
...
return (
<div className="Pokemons">
<div className="column pokemons-list">
<div className="wrapper">
{
pokemonsData.map((p, id) => (
<div className="box" key={ id }>
<img
src={ p.sprites.front_default }
alt="pokemon-img"
title={ p.name }
className="icon"
data-name={p.name}
/>
{ p.name}
<div className="container">
{ pokemonsTypes[id] }
</div>
</div>
))
}
</div>
...
</div>
<div className="column description">
{ pokemonDescription }
</div>
</div>
)
You should add pokemonDescription to your component state
const [pokemonDescription, setPokemonDescription] = useState([]);
Remove the forceUpdate function and hook, it is unnecessary.
Attach the click handlers to the elements with the data-name attribute you are trying to handle.
Map the pokemonDescription state array to renderable JSX. I simply used a div, but you should use whatever your UI design requires.
const onPokemonClick = (event) => {
setPokemonDescription(names => [
...names,
event.target.getAttribute('data-name'),
]);
};
...
return (
<div className="Pokemons">
<div className="column pokemons-list">
<div className="wrapper">
{
pokemonsData.map((p, id) => (
<div className="box" key={ id }>
<img
src={ p.sprites.front_default }
alt="pokemon-img"
title={ p.name }
className="icon"
data-name={p.name}
onClick={onPokemonClick} // <-- attach click handler to img element
/>
{ p.name}
<div className="container">
{ pokemonsTypes[id] }
</div>
</div>
))
}
</div>
...
</div>
<div className="column description">
{pokemonDescription.map(name => (
<div>{name}</div>
))}
</div>
</div>
)
Add pokemonDescription to state instead of some local variable and it will solve your issue.
Try to avoid using forceUpdate, most of the times it means only that you are doing something silly.
I don't what that useForceUpdate does , but here is how would go about adding pokemon names to description array which is a state variable in my answer
const [pokemonDescription , setPokemonDescription ] = useState(null);
const onPokemonClick = (p) => {
const tempPokemonDescription = [...pokemonDescription ];
pokemonDescription.push(p.name);
console.log("description array -", pokemonDescription);
setPokemonDescription(tempPokemonDescription )
};
...
return (
<div className="Pokemons">
<div className="column pokemons-list">
<div className="wrapper">
{
pokemonsData.map((p, id) => (
<div className="box" onClick={e=>onPokemonClick(p)} key={ id }>
<img
src={ p.sprites.front_default }
alt="pokemon-img"
title={ p.name }
className="icon"
/>
{ p.name}
<div className="container">
{ pokemonsTypes[id] }
</div>
</div>
))
}
</div>
...
</div>
<div className="column description">
{ pokemonDescription }
</div>
</div>
)

How to join an array with folder images in React

I am trying to render a child component with images from local folder, but I don't know how to do it.
So I have a const array with details about several projects. Each of the project has its own folder with images. The project name is equal folder name with images
Parent component
import { myProjects } from '../lib/Projects'; //its array with projects
export default class Parent extends Component {
render() {
// function for images
function importAll(r) {
return r.keys().map(r);
}
const projectA = importAll(require.context('../../assets/images/projectA', false, /\.(png|jpe?g|svg)$/));
const projects = myProjects.map((project, i) =>
<Child id={i} key={i} project={project} />)
return (
<div className="main-container">
{projects}
</div>
)
}
}
Child component
export default class Child extends Component {
render() {
const { project } = this.props;
return (
<div className="item">
<div className="desc">
<div className="item-name">
<p>{project.name}</p>
</div>
<div className="item-description">
<p>{project.description}</p>
</div>
<div className="item-tools">
<p>{project.tools}</p>
</div>
</div>
// this part works well
// How to make below part work?
<div className="image-block">
<div className="item-image-first">
<img src={project.name[0]} alt=""/>
</div>
<div className="item-images">
{project.name ? project.name.map((image, index) => {
return (
<div className="image-block-small" key={index}>
<ModalImage
small={image}
large={image}
alt=""
hideDownload={true}
hideZoom={true}
className="modal-image"
/>
</div>
)
})
: null }
</div>
</div>
</div>
)
}
}
Maybe there is a way to add an extra array here?
const projects = myProjects.map((project, i) =>
<Child id={i} key={i} project={project} />)
Any suggestion?

How do I add in an additional property for each individual mapped item after already making a call to a 3rd party API?

I want to add a feature that increments how many "likes" someone gets similar to FB. The profiles are getting passed in through an Axios GET request through a 3rd party API. When a user clicks on the like button, the amount of likes someone gets should increment by 1. The code I previously wrote in handleClicks() increments everyone's likes by 1 rather than just one individual person. The data is passed into cards[] in one chunk.
App.js
<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>
class App extends React.Component {
constructor(props) {
super(props)
this.state = {cards: [], numVotes: 0};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log("This is working!");
this.setState(numVotes: state.numVotes + 1})
}
componentDidMount() {
axios.get('/')
.then(res => {
this.setState({cards: res.data})
console.log(this.state);
})
}
render() {
return (
<div className="main-container">
<Header />
<CardList
cards={this.state.cards}
handleClick={this.handleClick}
/>
<hr className="ui divider"></hr>
</div>
);
}
export default App;
const CardList = props => {
const cards = props.cards.map(card => {
return <Card image={card.image_url}
name={card.name}
title={card.title}
blurb={card.bio}
handleClick={props.handleClick}
numVotes={props.numVotes}
/>
})
return <div className="ui divided items">
{cards}
</div>
}
Card.js
const Card = (props) => {
return (
<div className="card-component item">
<div class="ui small rounded image">
<img className="portrait"
src = {props.image}
onError={(e)=>{e.target.onerror = null; e.target.src='https://image.shutterstock.com/image-vector/no-image-available-vector-illustration-260nw-744886198.jpg'}}
/>
</div>
<div class="content">
<a className="header">{props.name}</a>
<div class="meta">
<span className="title">{props.title}</span>
</div>
<p className="blurb">{props.blurb}</p>
<p><span className="question"> Want to work with {props.name}?</span>
<span className="like-button" onClick={props.handleClick}>
<img className="icon" src={icon} />Yes!
</span>
</p>
<p className="yes-amt">{props.numVotes} people have said Yes!</p>
</div>
</div>
)
}
You need to first decide how to identify each card as unique so you can update the correct one. If you have an id that would be ideal, but I'm going to assume the name is unique since its a value in your question.
// pass the unique identifier to handler
handleClick(name) {
this.setState((prevState) => ({
// map over the previous cards and return a new array
cards: prevState.cards.map((card) => {
// If the name matches the current card, change it
if (card.name === name) {
return {...card, numVotes: card.numVotes + 1};
} else {
// Otherwise just return the same card unchanged.
return card;
}
})
}))
}
Then in your component use it like this:
// Use inline function so we can pass it a prop as a parameter
<span className="like-button" onClick={() => props.handleClick(props.name)}>

How can I target a specific div with onClick?

I'm building a NetFlix clone main page.
I have already the list of movies,tv series ecc.. rendered horizontal.
I'd like to click on the next arrow and slide only the relative section.
Now if I click the next arrow on the first section, the second one above scroll and I don't want that..
How can I achieve it?
I tried to google it and some tutorials but I can't find how to target elements in the same parent with onClick.
Thank you very much
class App extends React.Component {
state = { moviesNow: [], tvSeries: [], actionMovies: [], animation: [] };
containerSection = React.createRef();
handleClick = e => {
this.containerSection.current.style.transform = `translateX(-100%)`;
};
render() {
return (
<div className="container">
<h1 className="section-title">Now playing</h1>
<div className="wrapper">
<div className="nextArrow" onClick={this.handleClick}>
<img src={nextArrow} alt="" />
</div>
<div ref={this.containerSection} className="container-section">
<MovieItem movies={this.state.moviesNow} />
</div>
</div>
<h1 className="section-title">TV Series</h1>
<div className="wrapper">
<div className="nextArrow" onClick={this.handleClick}>
<img src={nextArrow} alt="" />
</div>
<div ref={this.containerSection} className="container-section">
<MovieItem movies={this.state.tvSeries} />
</div>
</div>
You are using only one ref for two different elements, I guess that is where you issue is coming from. So I updated handleClick to be able to receive a ref as an argument and return a function that will receive the event :
handleClick = ref => e => {
ref.current.style.transform = `translateX(-100%)`;
};
Then you can use handleClick this way:
<div className="nextArrow" onClick={this.handleClick(secondContainerSection)}>
<img src={nextArrow} alt="" />
</div>
Eventually it gives you something like this:
class App extends React.Component {
state = { moviesNow: [], tvSeries: [], actionMovies: [], animation: [] };
containerSection = React.createRef();
//a new ref
secondContainerSection = React.createRef();
handleClick = ref => e => {
ref.current.style.transform = `translateX(-100%)`;
};
render() {
return (
<div className="container">
<h1 className="section-title">Now playing</h1>
<div className="wrapper">
<div className="nextArrow" onClick={this.handleClick(containerSection)}>
<img src={nextArrow} alt="" />
</div>
<div ref={this.containerSection} className="container-section">
<MovieItem movies={this.state.moviesNow} />
</div>
</div>
<h1 className="section-title">TV Series</h1>
<div className="wrapper">
<div className="nextArrow" onClick={this.handleClick(secondContainerSection)}>
<img src={nextArrow} alt="" />
</div>
<div ref={this.secondContainerSection} className="container-section">
<MovieItem movies={this.state.tvSeries} />
</div>
</div>
make a seperate component for drawer and define the ref inside it
drawer Component
class Drawer extends React.Component {
containerSection =new React.createRef();
handleClick = e => {
this.containerSection.current.style.transform = `translateX(-100%)`;
};
render() {
return (
<div className="wrapper">
<div className="nextArrow" onClick={this.handleClick}>
<img src={nextArrow} alt="" />
</div>
<div ref={this.containerSection} className="container-section">
{this.props.children}
</div>
</div>
Main component
class App extends React.Component {
state = { moviesNow: [], tvSeries: [], actionMovies: [], animation: [] };
containerSection = React.createRef();
handleClick = e => {
this.containerSection.current.style.transform = `translateX(-100%)`;
};
render() {
return (
<div className="container">
<h1 className="section-title">Now playing</h1>
<Drawer><MovieItem movies={this.state.moviesNow} /></Drawer>
<h1 className="section-title">TV Series</h1>
<Drawer><MovieItem movies={this.state.tvSeries} /></Drawer>
</div>);

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