React onClick handler in map function - javascript

I've been searching the web for answers to my question, but without success. I am trying to add a simple react click handler to my button, but I can't seem to make it work. It is probably something really simple, I just can't wrap my head around it.
Here is my code:
export default class ReviewBox extends Component {
deleteReview() {
console.log("hey");
}
render() {
const {reviews, date, lectureId} = this.props;
const that = this;
return (
<div className="container content-sm">
<div className="headline"><h2>Reviews</h2> <span>{date}</span></div>
<div className="row margin-bottom-20">
{reviews.map(review => {
return(
<div className="col-md-3 col-sm-6">
<div className="thumbnails thumbnail-style thumbnail-kenburn">
<div className="caption">
<h3>{review.comment}</h3> <br />
<button className="btn btn-danger" onClick={this.deleteReview()}>Delete</button>
</div>
</div>
</div>
)
})}
</div>
<hr />
<AddReview lectureId={lectureId} />
</div>
)
}
}
It refuses to fire the function when I click a button. I've tried with .bind(this) and onClick={() => this.deleteReview} etc.
All help appreciated!

I think you are missing braces () in arrow function
<button className="btn btn-danger" onClick={() => this.deleteReview()}>Delete</button>

i think this will help you.....
export default class ReviewBox extends Component {
deleteReview() {
console.log("hey");
},
render() {
const {reviews, date, lectureId} = this.props;
const that = this;
return (
<div className="container content-sm">
<div className="headline"><h2>Reviews</h2> <span>{date}</span></div>
<div className="row margin-bottom-20">
{reviews.map(review => {
return(
<div className="col-md-3 col-sm-6">
<div className="thumbnails thumbnail-style thumbnail-kenburn">
<div className="caption">
<h3>{review.comment}</h3> <br />
<button className="btn btn-danger" onClick={this.deleteReview}>Delete</button>
</div>
</div>
</div>
)
})}
</div>
<hr />
<AddReview lectureId={lectureId} />
</div>
)
}
}

Removing () from the onClick function call and using { this.deleteReview } will indeed fire up the method, but if you need to bind this as well inside that method, go with #duwalanise answer.

Ah, now I understand.
It is because I am rendering the react on serverside, that's why the click handler doesn't work.
I will have to render the JS on the client, in order for it to work :)

Related

Typerror: object is not a function or its return value is not iterable in react 16.13.1

I am using react version 16.13.1. And i have used hooks alot of time in my project.but now am getting this error even before i use it.
export default function TourData(props) {
const [collapsed, setCollapsed] = useState();
const withinDatacollapse = props.tourWithin.slice(1);
const withinDatashow = props.tourWithin.slice(0, 1);
function handleViewAll() {
setCollapsed(!collapsed);
}
function Object(props) {
return (
<div>
{props.data &&
props.data.map((item, index) => (
<div key={index} className="within">
<div
className="image"
style={{ background: `url(${item.image})` }}
>
<div className="destination">{item.country}</div>
</div>
<div className="options">
<div>
<h5>Options</h5>
<div className="user-select">
<Select
options={item.options}
placeholder={<h4>Select Option</h4>}
/>
</div>
</div>
</div>
<div className="date-select">
<div className="user-select">
<Select
options={item.date_options}
placeholder={<h4>Select Date</h4>}
/>
</div>
</div>
<div>
<button>Go!</button>
</div>
</div>
))}
</div>
);
}
return (
<div className="route">
<div className="list-box">
<Object data={withinDatashow} />
<Collapse in={collapsed}>
<Object data={withinDatacollapse} />
</Collapse>
<div className="bottom-view-section">
<button onClick={handleViewAll}>View All</button>
</div>
</div>
</div>
);
}
Here i just declared the hook, and am getting this error.
I cant find where i have went wrong.
At first i had an error TypeError: Cannot read property 'map' of undefined where i do mapping , so i had to put props.data && just before the mapping.
You shouldn't name your components as Javascript's keywords like Object or class.
Also, you might want to define default value for props.placeData so when it is undefined, you don't get errors.
export default function AppMain({placeData = [], ...props}) {
Or you can just check in return, and say something like 'there is no place'. It's up to you.

ReactJs: refs is not defined (no-undef)

I have read the Refs and the Dom as well as try searching for any question or answer which could relate to my problem (I start with "function" instead of "class").
this is the problem name: 'refs' is not defined (no-undef) (at console.log(refs.okanhzai.value);)
and this is my code:
function ok123(){
console.log(refs.okanhzai.value);
}
<div className="panel panel-default">
<div className="panel-heading">
<h3 className="panel-title">Categories ok man</h3>
</div>
<div className="panel-body">
<div className="form-group">
<label >Search for it</label>
<input type="text" className="form-control" ref="okanhzai"/>
</div>
<button type="submit" className="btn btn-primary" onClick = { ok123() }> Save </button>
I am trying to print my input value to the console. If my code has any potential bug or error, please help me point it out at least. I will appreciate any further given help. ^_^. Thanks!
I think the html should be wrapped as a React Component first. Then you can start using ref attribute.
In the following code, I wrapped your code a function component and use React.createRef() to create a ref and assign to okanhzai. The okanhzai.current.value stores the current input.
const App = () => {
const okanhzai = React.createRef(null);
function ok123(){
console.log(okanhzai.current.value);
}
return (
<div>
<div className="panel panel-default">
<div className="panel-heading">
<h3 className="panel-title">Categories ok man</h3>
</div>
<div className="panel-body">
<div className="form-group">
<label >Search for it</label>
<input type="text" className="form-control" ref={okanhzai}/>
</div>
</div>
</div>
<button type="submit" className="btn btn-primary" onClick={ok123}> Save </button>
</div>
)
}
const container = document.querySelector('#root');
ReactDOM.render(<App />, container);
<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>
import "ref" from firebase/storage
import { ref } from 'firebase/storage';

How to update upvote counter for individual elements instead of all of them at once with React

Newbie dev learning React.
I'm trying to create an upvote functionality to a blog post in React but when I click on the upvote button I'm upvoting all of the blog post cards at once instead of the individual card.
How can I fix this? I believe the issue may be in the way I'm setting setState? But I may be wrong and looking for help.
Thanks in advance!
====
class Posts extends Component {
state= {
points: 0
}
componentDidMount() {
this.props.fetchPosts()
}
UNSAFE_componentWillReceiveProps(nextProps) {
if (nextProps.newPost) {
this.props.posts.unshift(nextProps.newPost);
}
}
handleClick = () => {
this.setState({points: this.state.points + 1})
}
render() {
const postItems = this.props.posts.map((post, index) => (
<div key={index} className="ui three stackable cards">
<div className="ui card">
<div className="content">
<div className="header">{post.title}</div>
<div className="meta"> {post.author}</div>
<div className="description">
<p>{post.body}</p>
</div>
</div>
<div className="extra content">
<i className="check icon"></i>
{this.state.points} Votes
</div>
<button className="ui button"
type="submit"
onClick={this.handleClick}>Add Point</button>
</div>
</div>
))
return (
<div>
<br />
<h2 className="ui header">
<i className="pencil alternate icon"></i>
<div className="content">
Blog Feed
<div className="sub header">Create New Post!</div>
</div>
</h2>
{postItems}
</div>
)
}
}
You have a single component storing the "points" state for all your posts. To achieve the functionality you described, each post should be it's own component with it's own state.
class Post extends Component {
state = {
points: 0
}
handleClick = () => {
this.setState({points: this.state.points + 1})
}
render = () =>
<div key={index} className="ui three stackable cards">
<div className="ui card">
<div className="content">
<div className="header">{this.props.title}</div>
<div className="meta"> {this.props.author}</div>
<div className="description">
<p>{this.props.body}</p>
</div>
</div>
<div className="extra content">
<i className="check icon"></i>
{this.state.points} Votes
</div>
<button className="ui button"
type="submit"
onClick={this.handleClick}>Add Point</button>
</div>
</div>
}
}
You are upvoting every card because you have only one counter. A separate counter should be defined for every card.
state = {}; // dictionary-a-like state structure
handleClick = (id) => () => {
this.setState((prevState) => ({
[id]: prevState[id] ? prevState[id] + 1 : 1, // check and increment counter
}));
}
onClick={this.handleClick(post.id)} // call function with post.id as argument
{this.state[post.id] || 0} Votes // display votes for every card
Note: I assumed that every card has it's own unique id, if not - index may come handy too.
You will need one counter for each post. Currently you only have a single counter for all posts, which means that they all display that same value.
The best way to achieve this would probably be to separate your post into its own component, and have that keep track of the counter.
The following solution uses a post ID (if you have it) to create a key in a stateful points object. Then, on click, you can add to the correct points key.
state = {
points: {}
}
handleClick = postId => {
this.setState({
points: {
...this.state.points,
[postId]: (this.state.points[postId] || 0) + 1
}
})
}
const postItems = this.props.posts.map((post, index) => (
...
<div className="extra content">
<i className="check icon"></i>
{this.state.points[post.id] || 0} Votes
</div>
<button
className="ui button"
type="submit"
onClick={() => this.handleClick(post.id)}
>
Add Point
</button>
...
)

React state change not causing re-render

I am trying to toggle the visibility of a div when clicking a seperate div. The problem is it sets the div invisible on the first click and only if it is visible to begin with. After that it just stays invisible and will not update. The state is still being toggled in the console however.
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(){
super()
this.state = {
vis: '0'
}
}
toghide=()=>{
console.log("toggle login", this.state.vis)
if(this.state.vis === "hidden"){
console.log('showing')
this.setState((state, props)=>({vis:'0'}))
} else {
console.log('hiding')
this.setState((state, props)=>({vis:'hidden'}))
}
}
render() {
const styles = {
visibility: this.state.vis
}
return (
<div className="App">
<div className="salebar"><a className="salebar sale"
href="login">Click here!</a></div>
<div className="navbar">
<div className="nav"><div className="nnav">JMZ</div></div>
<div className="nav2"><div className="nnav2">PRODUCTS</div></div>
<div className="loginContainer"><div className="login" onClick={this.toghide}>LOGIN/SIGN UP</div></div>
</div>
<div className="login-container">
<div className="lognester">
<div style={styles} className="login-tab">
<input className="user" type="text" placeholder="Username"/>
<input className="password" type="password" placeholder="Password"/>
<button className="user">Login</button>
Sign in or <a className="register-link" href="register">register</a> a new account.
</div>
</div>
</div>
<div className="intro-pics"></div>
<div className="content"></div>
<audio preload loop controls autoPlay className="audio">
<source src="https://memefly.me/i/toValhalla.mp3"/>
Your browser does not support the audio element.
</audio>
</div>
);
}
}
export default App;
Try this:
class App extends Component {
constructor() {
super();
this.state = {
vis: true
};
}
toghide = () => {
this.setState({ vis: !this.state.vis });
};
render() {
return (
<div className="App">
<div className="salebar">
<a className="salebar sale" href="login">
Click here
</a>
</div>
<div className="navbar">
<div className="nav">
<div className="nnav">JMZ</div>
</div>
<div className="nav2">
<div className="nnav2">PRODUCTS</div>
</div>
<div className="loginContainer">
<div className="login" onClick={this.toghide}>
LOGIN/SIGN UP
</div>
</div>
</div>
<div className="login-container">
<div className="lognester">
{this.state.vis ? (
<div className="login-tab">
<input className="user" type="text" placeholder="Username" />
<input
className="password"
type="password"
placeholder="Password"
/>
<button className="user">Login</button>
Sign in or{' '}
<a className="register-link" href="register">
register
</a>{' '}
a new account.
</div>
) : (
''
)}
</div>
</div>
<div className="intro-pics" />
<div className="content" />
</div>
);
}
}
export default App;
This is the demo: https://codesandbox.io/s/72zzk2xr70
Two things are wrong with your code
It should be visibility:visible not visibility: 0. So change that.
Never ever ever ever... set state in your render function, really really bad practice.
1 ) You can declare your togHide method above render such as
toghide = () => {
//your code
}
render(){...}
2) You may handle visibility condition better if you just use true/false boolean logic on your vis state. This can be as :
constructor(){
super()
this.state = {
vis: true
}
}
toghide = () => {
if(this.state.vis){
this.setState({
vis : false
})}
else{
this.setState({
vis : true
})}
3) In toghide method, you can use the code for setState I've used above. You don't have to pass props if you don't use any, and don't need to use return in setState.

How to pass props through a Javascript eval of a component?

I've got the following code:
Layout = React.createClass({
render() {
return (
<div>
<header>
<div className="container">
<button className="btn btn-sm pull-right"
onClick={this.handleLogout}>LogOut
</button>
</div>
</header>
<div className="container">
{this.props.content}
</div>
</div>
);
}
});
The Layout component serves as my shell. I pass it other "pages" which will get rendered with it (don't worry so much about this code, it's specific to Meteor):
ReactLayout.render(Layout, {content: <Home />});
My problem is, I want Layout to be able to pass props down to its children. In pseudo-code:
<div className="container">
{this.props.content someProp=this.props.someProp}
</div>
How do I go about doing this?
There a couple ways to go about passing the props to the children, but a simple way that we have a utility method for doing so is with React.cloneElement.
renderChildren() {
const someProps = { content : this.props.conent }// Filter props to pass
return React.Children.map(this.props.children, function(child) {
return React.cloneElement(child, someProps)
});
}
render() {
//...
<div className="container">
{this.renderChildren()}
</div>
//...
}
https://facebook.github.io/react/blog/2015/03/03/react-v0.13-rc2.html#react.cloneelement
As an example fiddle on how this looks and so you can test yourself: https://jsfiddle.net/jhuwhjp1/
~~ Note:
The previous way of doing this was React.addons.cloneWithProps which has been deprecated
Figured it out. Points to cdbitesky for pointing me in the right direction.
This render function in Layout will take whatever content I pass it (a React component) and pass it any of Layout's props.
Layout = React.createClass({
getDefaultProps() {
/*
* This will get passed down to whatever React component was passed to
* Layout via the prop "content"
*/
return {awesomeness: true}
},
render() {
return (
<div>
<header>
<div className="container">
<button className="btn btn-sm pull-right"
onClick={this.handleLogout}>LogOut
</button>
</div>
</header>
<div className="container">
{React.cloneElement(this.props.content, this.props)}
</div>
</div>
);
}
});

Categories