onClick event can't find function in react mapped object - javascript

While looping over an object using map() React can't find its own classes property!
Here is the class of the component,
import React, { Component } from 'react';
import './videolists.css';
export default class VideoLists extends Component {
constructor() {
super();
}
getDefaultLists() {
return [
{
title: 'Iridescent (Official Video) - Linkin Park',
url: 'https://www.youtube.com/watch?v=xLYiIBCN9ec',
id: 'xLYiIBCN9ec'
},
{
title: 'Ed Sheeran - I\'m A Mess (x Acoustic Sessions)',
url: 'https://www.youtube.com/watch?v=-t2CR9qZRj0',
id: '-t2CR9qZRj0'
},
{
title: 'Ed Sheeran - Lego House [Official Video]',
url: 'https://www.youtube.com/watch?v=c4BLVznuWnU',
id: 'c4BLVznuWnU'
}
]
}
itemSelected(itemObject) {
console.log(itemObject);
}
render() {
return (
<div>
<div className='panel panel-default'>
<div className='panel-heading'>
<ul className='list-group'>
{this.getDefaultLists().map(function(item, index){
return <li
key = { index }
className='list-group-item'
onClick={ this.itemSelected.bind(this) }>
{ item.title } <br/>
<small className='listurl'>{ item.url }</small>
</li>;
})}
</ul>
</div>
</div>
</div>
);
}
}
When a user would click on an item it should call the function called itemSelected and also binding the current this element with this.
But when the application is throughing and error.
Here is the error message:
Uncaught TypeError: Cannot read property 'itemSelected' of undefined(…)
How I can call this function in this case from the loop?

you are losing the this context because of your map function. not only do you need to bind that, to get the data object sent though you need to actually tell it to do that. like this.
<ul className='list-group'>
{this.getDefaultLists().map( (item, index) => {
return (
<li key ={index} className='list-group-item' onClick={() => this.itemSelected(item)}>
{ item.title }
<br/>
<small className='listurl'>{ item.url }</small>
</li>
);
})}
</ul>
you can try shadowing your this context, shouldn't be necessary, but worth a shot.
const self = this;
...
<ul className='list-group'>
{self.getDefaultLists().map( (item, index) => {
return (
<li key ={index} className='list-group-item' onClick={() => self.itemSelected(item)}>
{ item.title }
<br/>
<small className='listurl'>{ item.url }</small>
</li>
);
})}
</ul>

Related

How do I pass a mapping variable from parent to child in React

So I have just started learning React and its been fun so far, however I'm trying to split my components into sections for cleaner prettier code.
This is my component -
class Papers extends React.Component {
// Constructor
constructor(props) {
super(props);
this.state = {
items: [],
DataisLoaded: false
};
}
// ComponentDidMount is used to execute the code
componentDidMount() {
const randomFilmId = Math.floor(Math.random() * 10) + 1
const url ="#"
fetch(url)
.then((res) => res.json())
.then((json) => {
this.setState({
items:json,
DataisLoaded: true
});
})
.catch ((err) => {
console.log("something went wrong ", err)
});
}
render() {
const { DataisLoaded, items } = this.state;
if (!DataisLoaded) return <div>
<h1> Pleses wait some time.... </h1> </div> ;
return (
<div className="auth-box">
<div className="auth-card" key="{item.id}">
<div className = "App">
<h1> Fetch data from an api in react </h1> {
items.map((item) => (
<ul key = { item.id } >
<li> User_Name: { item.first_name }, </li>
<li> Full_Name: { item.middle_name }, </li>
<li> User_Email: { item.last_name } </li>
</ul>
))
}
</div>
</div>
</div>
);
}
}
export default Papers;
But if I want to put the following piece oof code in a new class,
<div className="auth-box">
<div className="auth-card" key="{item.id}">
<div className = "App">
<h1> Fetch data from an api in react </h1> {
items.map((item) => (
<ul key = { item.id } >
<li> User_Name: { item.first_name }, </li>
<li> Full_Name: { item.middle_name }, </li>
<li> User_Email: { item.last_name } </li>
</ul>
))
}
</div>
</div>
</div>
How would I pass all the props into the new class? I'm sorry if this a basic beginner question.
Appreciate all the help and support
You can pass down props to child components very easily.
<Auth userDetails={this.state.items} />
And iside the new class component you can access them as
this.props.userDetails
It is pretty simple actully. You just do
class AuthComponent extends React.Component {
render () {
return (
<div className="auth-box">
<div className="auth-card" key="{item.id}">
<div className = "App">
<h1> Fetch data from an api in react </h1>
{
this.props.items.map((item) => (
<ul key = { item.id } >
<li> User_Name: { item.first_name },</li>
<li> Full_Name: { item.middle_name }, </li>
<li> User_Email: { item.last_name } </li>
</ul>
))
}
</div>
</div>
</div>
);
}
}
and in parent do
<AuthComponent items={items} />

data from database not returning

In my react file, I need to check if my data variable is undefined or not in my DisplayCharacterDetails() function. However, when the function runs, I do not get the data.
When I console.log(data.character), I get the following:
{__typename: "Character", id: "5f4be9d5652cd07eda934rt6", name: "Iron Man", studio: "Marvel", affiliation: "Avengers", …}
However, when I do data.name, nothing prints out.
const {loading, data} = useQuery(getSingleCharacterQuery, {
variables: {id: props.characterID}
});
function DisplayCharacterDetails() {
console.log(data.character)
if(data) {
return (
<div>
<h2>{data.name}</h2>
<p>{data.studio}</p>
<p>{data.affiliation}</p>
<p>{data.protrayedBy}</p>
<p>{data.creator.name}</p>
<p>All characters by this creator: </p>
<ul className="other-characters">
{ data.creator.characters.map(item =>{
return <li key={item.id}> {item.name}</li>
})
}
</ul>
</div>
)
}
else {
return (
<div>
No Character Selected
</div>
)
}
}
Looks like to get the character object:
Console.log(data.character);
To get the character name:
console.log(data.character.name);
Looks like name is a property of character according to your object...
Ok the problem was to check if data.character is null or not. So, we need 2 if statements like so:
if(data && data.character) {
return (
<div>
<h2>name: {data.character.name}</h2>
<p>{data.character.studio}</p>
<p>{data.character.affiliation}</p>
<p>{data.character.protrayedBy}</p>
<p>{data.character.creator.name}</p>
<p>All characters by this creator: </p>
{/* <ul className="other-characters">
{ data.creator.characters.map(item =>{
return <li key={item.id}> {item.name}</li>
})
}
</ul> */}
</div>
)
The error can be not only in data.name but also in <p>{data.creator.name}</p> or in <li key={item.id}> {item.name}</li>. You can use the https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining to prevent reading from undefined property.
const {loading, data} = useQuery(getSingleCharacterQuery, {
variables: {id: props.characterID}
});
function DisplayCharacterDetails() {
console.log(data.character)
if(data) {
return (
<div>
<h2>{data.name}</h2>
<p>{data.studio}</p>
<p>{data.affiliation}</p>
<p>{data.protrayedBy}</p>
<p>{data.creator?.name}</p>
<p>All characters by this creator: </p>
<ul className="other-characters">
{ data.creator.characters.map(item =>{
return <li key={item.id}> {item?.name}</li>
})
}
</ul>
</div>
)
}
else {
return (
<div>
No Character Selected
</div>
)
}
}

Cannot read property left of undefined. Jquery Error [duplicate]

I have
var TestApp = React.createClass({
getComponent: function(){
console.log(this.props);
},
render: function(){
return(
<div>
<ul>
<li onClick={this.getComponent}>Component 1</li>
</ul>
</div>
);
}
});
React.renderComponent(<TestApp />, document.body);
I want to color the background of the clicked list element. How can I do this in React ?
Something like
$('li').on('click', function(){
$(this).css({'background-color': '#ccc'});
});
Why not:
onItemClick: function (event) {
event.currentTarget.style.backgroundColor = '#ccc';
},
render: function() {
return (
<div>
<ul>
<li onClick={this.onItemClick}>Component 1</li>
</ul>
</div>
);
}
And if you want to be more React-ive about it, you might want to set the selected item as state of its containing React component, then reference that state to determine the item's color within render:
onItemClick: function (event) {
this.setState({ selectedItem: event.currentTarget.dataset.id });
//where 'id' = whatever suffix you give the data-* li attribute
},
render: function() {
return (
<div>
<ul>
<li onClick={this.onItemClick} data-id="1" className={this.state.selectedItem == 1 ? "on" : "off"}>Component 1</li>
<li onClick={this.onItemClick} data-id="2" className={this.state.selectedItem == 2 ? "on" : "off"}>Component 2</li>
<li onClick={this.onItemClick} data-id="3" className={this.state.selectedItem == 3 ? "on" : "off"}>Component 3</li>
</ul>
</div>
);
},
You'd want to put those <li>s into a loop, and you need to make the li.on and li.off styles set your background-color.
Two ways I can think of are
var TestApp = React.createClass({
getComponent: function(index) {
$(this.getDOMNode()).find('li:nth-child(' + index + ')').css({
'background-color': '#ccc'
});
},
render: function() {
return (
<div>
<ul>
<li onClick={this.getComponent.bind(this, 1)}>Component 1</li>
<li onClick={this.getComponent.bind(this, 2)}>Component 2</li>
<li onClick={this.getComponent.bind(this, 3)}>Component 3</li>
</ul>
</div>
);
}
});
React.renderComponent(<TestApp /> , document.getElementById('soln1'));
This is my personal favorite.
var ListItem = React.createClass({
getInitialState: function() {
return {
isSelected: false
};
},
handleClick: function() {
this.setState({
isSelected: true
})
},
render: function() {
var isSelected = this.state.isSelected;
var style = {
'background-color': ''
};
if (isSelected) {
style = {
'background-color': '#ccc'
};
}
return (
<li onClick={this.handleClick} style={style}>{this.props.content}</li>
);
}
});
var TestApp2 = React.createClass({
getComponent: function(index) {
$(this.getDOMNode()).find('li:nth-child(' + index + ')').css({
'background-color': '#ccc'
});
},
render: function() {
return (
<div>
<ul>
<ListItem content="Component 1" />
<ListItem content="Component 2" />
<ListItem content="Component 3" />
</ul>
</div>
);
}
});
React.renderComponent(<TestApp2 /> , document.getElementById('soln2'));
Here is a DEMO
I hope this helps.
Here is how you define a react onClick event handler, which was answering the question title... using es6 syntax
import React, { Component } from 'react';
export default class Test extends Component {
handleClick(e) {
e.preventDefault()
console.log(e.target)
}
render() {
return (
<a href='#' onClick={e => this.handleClick(e)}>click me</a>
)
}
}
Use ECMA2015. Arrow functions make "this" a lot more intuitive.
import React from 'react';
class TestApp extends React.Component {
getComponent(e, index) {
$(e.target).css({
'background-color': '#ccc'
});
}
render() {
return (
<div>
<ul>
<li onClick={(e) => this.getComponent(e, 1)}>Component 1</li>
<li onClick={(e) => this.getComponent(e, 2)}>Component 2</li>
<li onClick={(e) => this.getComponent(e, 3)}>Component 3</li>
</ul>
</div>
);
}
});
React.renderComponent(<TestApp /> , document.getElementById('soln1'));`
If you're using ES6, here's some simple example code:
import React from 'wherever_react_is';
class TestApp extends React.Component {
getComponent(event) {
console.log('li item clicked!');
event.currentTarget.style.backgroundColor = '#ccc';
}
render() {
return(
<div>
<ul>
<li onClick={this.getComponent.bind(this)}>Component 1</li>
</ul>
</div>
);
}
}
export default TestApp;
In ES6 class bodies, functions no longer require the 'function' keyword and they don't need to be separated by commas. You can also use the => syntax as well if you wish.
Here's an example with dynamically created elements:
import React from 'wherever_react_is';
class TestApp extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [
{name: 'Name 1', id: 123},
{name: 'Name 2', id: 456}
]
}
}
getComponent(event) {
console.log('li item clicked!');
event.currentTarget.style.backgroundColor = '#ccc';
}
render() {
<div>
<ul>
{this.state.data.map(d => {
return(
<li key={d.id} onClick={this.getComponent.bind(this)}>{d.name}</li>
)}
)}
</ul>
</div>
);
}
}
export default TestApp;
Note that each dynamically created element should have a unique reference 'key'.
Furthermore, if you would like to pass the actual data object (rather than the event) into your onClick function, you will need to pass that into your bind. For example:
New onClick function:
getComponent(object) {
console.log(object.name);
}
Passing in the data object:
{this.state.data.map(d => {
return(
<li key={d.id} onClick={this.getComponent.bind(this, d)}>{d.name}</li>
)}
)}
Handling events with React elements is very similar to handling events
on DOM elements. There are some syntactic differences:
React events are named using camelCase, rather than lowercase.
With JSX you pass a function as the event handler, rather than a string.
So as mentioned in React documentation, they quite similar to normal HTML when it comes to Event Handling, but event names in React using camelcase, because they are not really HTML, they are JavaScript, also, you pass the function while we passing function call in a string format for HTML, they are different, but the concepts are pretty similar...
Look at the example below, pay attention to the way event get passed to the function:
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}
return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}
import React from 'react';
class MyComponent extends React.Component {
getComponent(event) {
event.target.style.backgroundColor = '#ccc';
// or you can write
//arguments[0].target.style.backgroundColor = '#ccc';
}
render() {
return(
<div>
<ul>
<li onClick={this.getComponent.bind(this)}>Component 1</li>
</ul>
</div>
);
}
}
export { MyComponent }; // use this to be possible in future imports with {} like: import {MyComponent} from './MyComponent'
export default MyComponent;
class FrontendSkillList extends React.Component {
constructor() {
super();
this.state = { selectedSkill: {} };
}
render() {
return (
<ul>
{this.props.skills.map((skill, i) => (
<li
className={
this.state.selectedSkill.id === skill.id ? "selected" : ""
}
onClick={this.selectSkill.bind(this, skill)}
style={{ cursor: "pointer" }}
key={skill.id}
>
{skill.name}
</li>
))}
</ul>
);
}
selectSkill(selected) {
if (selected.id !== this.state.selectedSkill.id) {
this.setState({ selectedSkill: selected });
} else {
this.setState({ selectedSkill: {} });
}
}
}
const data = [
{ id: "1", name: "HTML5" },
{ id: "2", name: "CSS3" },
{ id: "3", name: "ES6 & ES7" }
];
const element = (
<div>
<h1>Frontend Skill List</h1>
<FrontendSkillList skills={data} />
</div>
);
ReactDOM.render(element, document.getElementById("root"));
.selected {
background-color: rgba(217, 83, 79, 0.8);
}
<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>
#user544079 Hope this demo can help :) I recommend changing background color by toggling classname.
import React from 'react';
class MyComponent extends React.Component {
getComponent(event) {
event.target.style.backgroundColor = '#ccc';
// or you can write
//arguments[0].target.style.backgroundColor = '#ccc';
}
render() {
return(
<div>
<ul>
<li onClick={this.getComponent.bind(this)}>Component 1</li>
</ul>
</div>
);
}
}
export { MyComponent }; // use this to be possible in future imports with {} like: import {MyComponent} from './MyComponent'
export default MyComponent;
You can make use of the React.createClone method. Create your element, than create a clone of it. During the clone's creation, you can inject props. Inject an onClick : method prop like this
{ onClick : () => this.changeColor(originalElement, index) }
the changeColor method will set the state with the duplicate, allowing you sto set the color in the process.
render()
{
return(
<ul>
{this.state.items.map((val, ind) => {
let item = <li key={ind}>{val}</li>;
let props = {
onClick: () => this.Click(item, ind),
key : ind,
ind
}
let clone = React.cloneElement(item, props, [val]);
return clone;
})}
</ul>
)
}
This is a non-standard (but not so uncommon) React pattern that doesn't use JSX, instead putting everything inline. Also, it's Coffeescript.
The 'React-way' to do this would be with the component's own state:
(c = console.log.bind console)
mock_items: [
{
name: 'item_a'
uid: shortid()
}
{
name: 'item_b'
uid: shortid()
}
{
name: 'item_c'
uid: shortid()
}
]
getInitialState: ->
lighted_item: null
render: ->
div null,
ul null,
for item, idx in #mock_items
uid = item.uid
li
key: uid
onClick: do (idx, uid) =>
(e) =>
# justf to illustrate these are bound in closure by the do lambda,
c idx
c uid
#setState
lighted_item: uid
style:
cursor: 'pointer'
background: do (uid) =>
c #state.lighted_item
c 'and uid', uid
if #state.lighted_item is uid then 'magenta' else 'chartreuse'
# background: 'chartreuse'
item.name
This example works -- I tested it locally.
You can check out this example code exactly at my github.
Originally the env was only local for my own whiteboard r&d purposes but I posted it to Github for this. It may get written over at some point but you can check out the commit from Sept 8, 2016 to see this.
More generally, if you want to see how this CS/no-JSX pattern for React works, check out some recent work here. It's possible I will have time to fully implement a POC for this app idea, the stack for which includes NodeJS, Primus, Redis, & React.

Set state for only for self data into map on reactjs

I have a object's array of users and i'm using map to show them, each user have a option buttons that is 'edit' and 'remove' options each option have a onlclick function that set a state to show another view so the code explain itselft
class App extends React.Component {
state = {
edit: false,
remove: false
}
handleEdit = () => {
this.setState({ edit: true })
}
handleRemove = () => {
this.setState({ remove: true })
}
cancelEdit = () => {
this.setState({ edit: false })
}
cancelRemove = () => {
this.setState({ remove: false })
}
renderEditItem = () => {
const {
state: {
edit,
remove
},
cancelEdit,
cancelRemove,
handleEdit,
handleRemove
} = this
if (edit) {
return (
<div>
<span>Edit view</span>
<br/>
<button onClick={cancelEdit}>Cancel</button>
</div>
)
}
if (remove) {
return (
<div>
<span>Remove view</span>
<br/>
<button onClick={cancelRemove}>Cancel</button>
</div>
)
}
return (
<div>
<button onClick={handleEdit}>Edit</button>
<br/>
<button onClick={handleRemove}>Remove</button>
</div>
)
}
renderUsers = () => {
const {
renderEditItem
} = this
const users = [
{
id: 1,
name: 'User1'
},
{
id: 2,
name: 'User-2'
},
{
id: 3,
name: 'User-3'
}
]
return users.map((user) => {
return (
<ul key={user.id}>
<li>
<div>
<span ref='span'>{user.name}</span>
<br/>
{renderEditItem()}
</div>
</li>
</ul>
)
})
}
render () {
return (
<div>
{this.renderUsers()}
</div>
)
}
}
React.render(
<App />,
document.getElementById('app')
);
JSfiddle: Here
The issue is how can you see is, when i click on the button to set the state for edit or remove option, this will show the view for all the items,
and should be only the view that is clicked, i know the state change to true and is the same for all the items but i don't know how to set the state only for one entry any idea?
Thank you in advance.
Your problem is that the edit/remove state is singular and for the entire list. Each item in the list receives the same state here:
if (edit) {
return (
<div>
<span>Edit view</span>
<br/>
<button onClick={cancelEdit}>Cancel</button>
</div>
)
}
The single edit variable from the state is applied to each list item. If you want to individually set the edit state for each item, it will need to be kept track of with that item.
EX:
const users = [
{
id: 1,
name: 'User1',
edit: true
}]
This way each individual item will be able to tell what state it is in individually. User1 item will have an edit mode that is independent of the other users.
Then you can render something like this:
return users.map((user) => {
return (
<ul key={user.id}>
<li>
<div>
<span ref='span'>{user.name}</span>
<br/>
{user.edit ? 'EDIT MODE' : 'NOT EDIT MODE'}
</div>
</li>
</ul>
)
})

react.js and transmission parameter props in recursion

I wont transmission parameter props in recursion:
class App extends React.Component {
constructor(props) {
super(props);
this.state = { visible: this.props.root, a:this.props.a };
}
toggle(){ this.setState({visible: !this.state.visible}); }
render() {
let child, classObj;
if (this.props.node.child != null) {
child = this.props.node.child.map(function(node, index) { return <li key={index}><App a={this.state.a} node={node} /></li> });
classObj = {togglable:true, "togglable-down":this.state.visible, "togglable-up":!this.state.visible };
}
let style;
if (!this.state.visible) style = {display:"none"}
return (
<div id="tree">{ !this.props.root &&
<a style={{cursor:"pointer"}} onClick={this.toggle.bind(this)} className={this.props.node.child.length!=0 ? classNames(classObj) : 'togglable' }>
{this.props.node.child.length!=0 ? this.props.node.title : <label ><input type="checkbox" /> {this.props.node.title} </label>}
</a>}
<ul style={style}> {child} </ul>
</div>
);
}
}
const tree =
{"_id":"_", "child":[
{"_id":"029", "title":"One title",
"child":[
{"_id":"a01", "title":"Two title", "child": []},
{"_id":"8a5", "title":"News", "child": []},
{"_id":"02e", "title":"ACL",
"child": [{"_id":"0b0", "title":"Last Title", "child":[]}]}
]
}
]};
React.render( <App node={tree} root={true} a={true}/>, document.getElementById("app"));
But I get an error:
Uncaught TypeError: Cannot read property 'state' of undefined
In the line where the class of recursively calls itself, I am trying to pass the value props stored in the this.state:
<li key={index}><App a={this.state.a} node={node} /></li>
Code on codepen:
https://codepen.io/alex183/pen/ygEJwd
How best can pass into recursion props?
Because you are using an anonymous function for the map, 'this' does not refer to the current App class. If you console.log it you'll see it is undefined. You can either change it to an arrow function which preserves the 'this' context:
.map((node, index) => { /* Same as before */ }
Or you can copy the value before entering the function
const a = this.state.a
// Same as before
.map((node, index) => { /* Same as before, but now a={a} */ }
Or you can pass 'this' as the second parameter to map and it will set the context for you:
.map(function(node, index) { /* Same as before */}, this);

Categories