Bind click to a div and get attribute data in React - javascript

There are multiple divs on a page. What I want to do is get an attribute when a div is clicked. It is very easy in jquery but I am using Reactjs. Here is my code. It works but every time when I click on div attribute of the last element in is returned. Following is my code and the codepen URL.
https://codepen.io/anon/pen/gepVNP?editors=0010
class Content extends React.Component{
constructor(props) {
super(props)
this.click = this.click.bind(this)
}
click(){
// this.prop.setActiveMenu();
var summary = this.refs.summary;
console.log(summary.getAttribute('data-slug'))
}
render(){
return(
<div className="content">
{posts.map((post)=>{
return (
<div ref="summary" data-slug={post.slug} onClick={this.click} key={post.slug}>
<h1>{post.title}</h1>
<div>{post.content}</div>
<br/><br/><br/><br/><br/>
</div>
);
})}
</div>
);
}
}

That's because you're changing the ref element inside the map everytime you iterate on the posts array.
No need for refs here IMO.
Why not use the event.target from the click event?
onClick(event){
console.log(event.target.getAttribute('data-slug'))
}
BTW:
String refs are considered legacy. have a look here:
Why ref='string' is "legacy"?

I discourage you from using this approach, you could use a component instead.
The ref prop accepts a function and return the ref, so passing a string doesn't work.
class Content extends React.Component{
constructor(props) {
super(props)
this.click = this.click.bind(this)
}
click(){
console.log(this.summary.getAttribute('data-slug'))
}
render(){
return(
<div className="content">
{posts.map((post)=>{
return (
<div ref={ref => this.summary = ref} data-slug={post.slug} onClick={this.click} key={post.slug}>
<h1>{post.title}</h1>
<div>{post.content}</div>
<br/><br/><br/><br/><br/>
</div>
);
})}
</div>
);
}
}

Related

show more and show less dinamicaly for each div

when I click the particular show more button the content should be displayed, the condition is the whole component should not re-rendering
I have used a useState when I clicked the button it is re-rendering the whole component
it is taking a long time to re-render every div
give an easy solution for this.
const [arr,setmarr] =useState([])
const oncl=(e)=>{
setarr((prev)=>[...prev,e.target.value])
}
return{
divarray.map((i,j)=>{
{console.log("tdic)")}
return(
<Commentbox divarr={arr[j]} value={j} oncl={(e)=>oncl(e) } />
)
}
}
Commentbox component
return
<div>
div{j}
// some icons here
{divarr && <div> right side div </div>}
<button onClick={(e)=>{oncl(e)}} value={j} >see more</button >
</div>
before onClick on showmore
after showmore button has been clicked on the second div
you should change each box to a component to solve this problem.
make that component with class base component because you need getSanpShotBeforeUpadte.
getSanpShotBeforeUpadte: you can control your component's render with this method.dont forget this method will give you nextProps,nextState and snapshot as parameter
class Box extends Component{
state = {
// more
showMore: false,
}
getSnapshotBeforeUpdate(nextProps,nextState){
// OTHER CONDITIONS
if(nextState.showMore !== this.state.showMore) return true
return false
}
render(){
return (
<div>
{/* CODE ... */}
<div style={{display: this.state.showMore ? 'block' : 'none'}}>
HERE IS A TEXT
</div>
<button onClick={()=>this.setState({showMore: !this.state.showMore})}>show more</button>
</div>
)
}
}

How to append or insert an HTML element in existing DOM in React

How to append or insert an HTML element in existing DOM(any element in DOM) on a click of a button? Had it appended, how to populate yet be written content after click of button in that appended element?
I know in Jquery it's a piece of cake but I want React to help me out this time.
Here is the code : i want to populate add <p></p> in 'editor' div and populate content what i will write after clicking button.
Any help would be appreciated.
export default class App extends React.Component{
constructor(props){
super(props);
}
parargraph(e){
const parargraph = '<p></p>';
alert(parargraph);
}
render(){
return(
<div className={"editorHolder " + (this.props.active ? 'show' : '' ) } onClick={this.hideEditor}>
<div className="editBar">
<ul>
<li><button onClick={this.parargraph}>Parargraph</button></li>
<li><button>H1</button><button>H2</button><button>H3</button><button>H4</button><button>H5</button><button>H6</button></li>
<li><button>Image</button></li>
<li><button>Link</button></li>
<li><button>List</button></li>
<li><button>Audio</button></li>
<li><button>Video</button></li>
</ul>
</div>
<div className="editor">
</div>
</div>
);
}
}
React offers a way to directly manipulate the innerHTML of a div.
Check out dangerouslySetHTML
For your code, try this:
export default class App extends React.Component{
constructor(props){
super(props);
this.state={
content: ''
}
this.paragraph = this.paragraph.bind(this)
}
paragraph(e){
const paragraph = '<p></p>'
let content = this.state.content
content += paragraph
this.setState({content})
}
render(){
return(
<div className={"editorHolder " + (this.props.active ? 'show' : '' ) } onClick={this.hideEditor}>
<div className="editBar">
<ul>
<li><button onClick={this.paragraph}>Paragraph</button></li>
<li><button>H1</button><button>H2</button><button>H3</button><button>H4</button><button>H5</button><button>H6</button></li>
<li><button>Image</button></li>
<li><button>Link</button></li>
<li><button>List</button></li>
<li><button>Audio</button></li>
<li><button>Video</button></li>
</ul>
</div>
<div className="editor" dangerouslySetInnerHTML={{__html: this.state.content}}>
</div>
</div>
);
}
}
Explanation: You would want to keep the content of the editor in the state, ready to change and append more content upon clicking of any of the buttons. The editor div will receive the state and updates its value upon state change.
PS: It's good practice to bind your component methods in the constructor, I've done it for you in the sample code.
You also misspelled 'paragraph'.

scrollIntoView using React refs

We are trying to scroll to a specific component when the user closes another component.
Our example is very similar to that down below, taken from https://reactjs.org/docs/refs-and-the-dom.html#exposing-dom-refs-to-parent-components
function CustomComponents(props) {
const items = [1,2,3,4].map((x, i) => return (
<div ref={props.inputRef} key={i}>
x + hello
</div>
)
return (
<div>
{ items }
</div>
);
}
function Parent(props) {
return (
<div>
<CustomComponents inputRef={props.inputRef} />
</div>
);
}
class Grandparent extends React.Component {
render() {
return (
<Parent
inputRef={el => this.inputElement = el}
/>
);
}
}
We are rendering a big list of cards and want to be able to scroll to a specific card by calling this.refs[elem].scrollIntoView()
But our problem is that calling this.refs returns an empty object at all levels, and so we are unable to attach to a specific element and then fire it when the user arrives back to view this component.
Would like some help on how one would go about solving this issue.
Where are you trying to call this.refs[elem].scrollIntoView()? You should be working with refs inside componentDidMount or componentDidUpdate, not inside the render method.
I've solved my specific issue by providing an if statement written below in my Grandparent component.
inputRef={el => {
if (el && el.id == this.state.selectedBuildingRef) {
this.myelement.scrollIntoView();
window.scrollBy(0, -65);
}
}}

dynamically render a unique button on a react component that is being used several times on a page

I'm new to react and have to make a project for my bootcamp with it and I am having trouble getting every movie component I render to have an individual button. Every time I click one button, the rest of the buttons on the page act like they are also clicked. Here is my movie component that is being called on.
Heres my first row of components and the buttons are the green ones on the bottom left corner. https://files.slack.com/files-pri/T571CRHGE-F826BKX7S/api.png.
importReact, { Component} from"react";
importAPIfrom"../utils/API"
classMovieextendsComponent{
constructor(){
super();
this.state={
color:'green',
icon:'add',
result:[]
};
}
componentDidMount() {
this.topMovies();
}
topMovies=() =>{
API.topMovies()
.then(res=>this.setState({ result:res.data.results}))
.catch(err=>console.log(err));
}
handleClick=event=>{
if(this.state.color==='green'){
this.setState({color:'red'});
} else{
this.setState({color:'green'});
}
if(this.state.icon==='add') {
this.setState({icon:'remove'});
} else{
this.setState({icon:'add'});
}
}
render() {
constimgURL="https://image.tmdb.org/t/p/w300/"
return(
<div>
{
this.state.result.map((movieList) =>(
<div className="col s4 movieBox">
<div className="card">
<div className="card-image">
<img src={imgURL +movieList.poster_path} />
<span className="card-title"><a id={this.state.color} onClick={this.handleClick} className="btn-floating btn waves-effect waves-light"><i className="material-icons">{this.state.icon}</i></a></span>
</div>
<div className="card-content movieInfo">
<p>Title:{movieList.title}</p>
<p>Genre:{movieList.genre_ids}</p>
<p>Rating:{movieList.vote_average}</p>
</div>
</div>
</div>
))
}
</div>
)
}
}
exportdefaultMovie;
You need to bind the handleClick function ( in fact all functions ) inside the constructor:
constructor(){
super();
this.state={
color:'green',
icon:'add',
result:[]
};
this.handleClick = this.handleClick.bind(this);
}
onClick={()=> this.handleClick()} will also work.
.map() calls a provided callback for each element in your array. So this means that you are creating several buttons that will execute the same event on each of your elements. I suggest creating a row component that handles your onClickEvent and then you could pass the component an id or use an in line arrow function () => this.handleClick(). (this does create a anonymous function on each click and could potentially be bad for performance in larger apps but could work in your case if you don't want to create a row component)
Also you should read up on why using keys with unique ids is important.
https://coderwall.com/p/jdybeq/the-importance-of-component-keys-in-react-js
Hope that helps.

Get cursor position in a sibling component

Suppose I have two components:
class App extends Component {
insertToStory = (word) => {
// how to get the cursor position here?
}
render() {
return (
<div>
<StoryTextarea text={this.props.text} />
<Toolbar insert={this.insertToStory} />
</div>
)
)
}
the StoryTextarea contains a textarea, and Toolbar contains a button, when clicked, we should insert some word to the textarea under the currrent cursor position. but how can I get the cursor position in insertToStory? or is there other ways to implement this?
Using refs is a good option to achieve that.
1º Add a new method in your StoryTextArea component to get the cursor position.
class StoryTextArea extends React.Component{
constructor(props) {
super(props);
this.getCursorPosition = this.getCursorPosition.bind(this);
}
getCursorPosition(){
return this.refs.textarea.selectionStart;
}
render(){
return <div>
<textarea ref="textarea"/>
</div>
}
}
2º Add a ref to the StoryTextArea component
<StoryTextarea ref="storyTextArea" text={this.props.text} />
3º Call getCursorPosition using this.refs.storyTextArea.getCursorPosition()
insertToStory = (word) => {
let position = this.refs.storyTextArea.getCursorPosition();
}
jsfiddle example

Categories