React: Rendering a method defined inside arrow function? - javascript

Hello friends! I hope you are well.
I've got an arrow function called WorldInfo and its parent component is passing down an object in props that for the sake of this example, I'm just calling object. Now In WorldInfo I also want to parse and list the items in object, so I've created the method serverInfoTabList to take object and shove it through .map. My problem is when compiled, my browser does not recognize serverInfoTabList either when it's defined nor called in WorldInfo's own return function.
Here is the error and the code itself.
Line 7:5: 'serverInfoTabList' is not defined no-undef
Line 34:22: 'serverInfoTabList' is not defined no-undef
const WorldInfo = (props) => {
serverInfoTabList = (object) => {
if (object != undefined){
return object.item.map((item) => {
const time = Math.trunc(item.time/60)
return (
<li key={item._id}>{item.name}
<br/>
Minutes Online: {time}
</li>
);
});
}
}
return (
props.object!= undefined ?
<div className={props.className}>
<h1>{props.world.map}</h1>
{/* <img src={props.object.image}/> */}
<div>
<ul>
{serverInfoTabList(props.object)}
</ul>
</div>
</div>
:
null
);
}
Thanks for your time friendos!

You forgot the const declaration
const serverInfoTabList = (object) => {
/* ... */
}
The other problem is that you're accessing properties which doesn't exist props.world for instance. Also you're mapping through an undefined property props.object.item. I've corrected your sandbox
const WorldInfo = props => {
const serverInfoTabList = object => {
return Object.keys(object).map(key => {
const item = object[key];
const time = Math.trunc(item.time / 60);
return (
<li key={item._id}>
{item.name}
<br />
Minutes Online: {time}
</li>
);
});
};
return props.object ? (
<div className={props.className}>
<h1>{props.world.map}</h1>
{/* <img src={props.object.image}/> */}
<div>
<ul>{serverInfoTabList(props.object)}</ul>
</div>
</div>
) : null;
};
class Todo extends Component {
render() {
const object = { item1: { _id: 1, time: 1 }, Item2: { _id: 2, time: 2 } };
return (
<div>
<WorldInfo object={object} world={{ map: "foo" }} />
</div>
);
}
}

Related

map in react not displaying data with { } (curly brackets)

So I am trying to return the data from API by map and when I am using ( ) these brackets I am getting the data when I use { } to put if statement, I am getting nothing on my web page but still getting the data in console
const Addtocart = () => {
const data = useSelector((state) => state);
console.log("products", data.productData);
const dispatch = useDispatch();
useEffect(() => {
dispatch(productList());
}, []);
return (
<div id="addtocart-info">
<div className="products">
{data.productData.map((item) => { // here this bracket
if (item.id % 2 === 0 || item.id === 0) {
<div key={item.id} className="product-item">
<img src={item.photo} alt="" />
<div>Name : {item.name} </div>
<div>Color : {item.color} </div>
<button onClick={() => dispatch(addToCart(item))}>
ADD to Cart
</button>
</div>;
console.warn(item.id);
} else {
console.log(item.id);
}
})}
</div>
</div>
);
};
export default Addtocart;
Is there any way to put if statement with () or make this work
You are not getting anything because when u use {} you have to use a return keyword, but when you are using () you don't have to use a return keyword because the whole code inside this is considered as a single piece of code even if it's distributed in multiple lines
so change your code to ,
{data.productData.map((item) => { // here this bracket
if (item.id % 2 === 0 || item.id === 0) {
return (
<div key={item.id} className="product-item">
<img src={item.photo} alt="" />
<div>Name : {item.name} </div>
<div>Color : {item.color} </div>
<button onClick={() => dispatch(addToCart(item))}>
ADD to Cart
</button>
</div>
)
} else {
console.log(item.id);
}
})}
If you use curly brackets you also need to use a return statement. Basically if you don't use curly brackets in an arrow function the statement is returned automatically.
Example:
let x = someArray.map(x => x*2); // returns a new array with the expression applied
let x = someArray.map(x => {return x * 2}) // use the return here

How to render a React component inside of itself

I'm learning React and I'm trying to render the <Comment/> component inside of it self, however I get the following error:
TypeError: Cannot read property 'map' of undefined
Comment._this.getResponses
src/Comment.js:28
25 | );
26 | }
27 | getResponses = () => {
> 28 | return this.props.responses.map(p => {
| ^ 29 | return (
30 | <Comment
31 | author={p.author}
and the code:
import React, { Component } from "react";
class Comment extends Component {
render() {
return (
<div className="comment">
<a className="avatar">
<img src={this.props.avatar} />
</a>
<div className="content">
<a className="author">{this.props.author}</a>
<div className="metadata">
<span className="date">{this.props.timeStamp}</span>
</div>
<div className="text">
<p>{this.props.text}</p>
</div>
<div className="actions">
<a className="reply">Reply</a>
</div>
</div>
<div className="comments">{this.getResponses()}</div>
</div>
);
}
getResponses = () => {
return this.props.responses.map(p => {
return (
<Comment
author={p.author}
avatar={p.avatar}
timeStamp={p.timeStamp}
text={p.text}
/>
);
});
};
}
export default Comment;
Please note that this.props.responses is not undefined, and the problem only occurs while I'm trying to use the Comment component. If I replace the Comment component here:
return this.props.responses.map(p => {
return <Comment
author={p.author}
avatar={p.avatar}
timeStamp={p.timeStamp}
text={p.text}
/>
});
with something like this:
return this.props.responses.map(p => {
return (
<div>
<h1>author={p.author}</h1>
<h1>avatar={p.avatar}</h1>
<h1>timeStamp={p.timeStamp}</h1>
<h1>text={p.text}</h1>
</div>
);
});
the code works.
This is because the rendering of <Comment /> relies on the responses prop being defined.
Currently, when you render Comment components in getResponses(), there is no responses prop assigned to those comments:
<Comment
author={p.author}
avatar={p.avatar}
timeStamp={p.timeStamp}
text={p.text}
/>
This in turn means an error will be thrown when these <Comment /> components are rendered, and they attempt to render "children" of their own (during the call to getResponses()) via the undefined responses prop.
To resolve this, you can check to see that the this.props.responses array is defined before proceeding to map and render <Comment/> components in the getResponses() method, like so:
getResponses = () => {
// Check that responses prop is an array before
// attempting to render child Comment components
if(!Array.isArray(this.props.responses)) {
return null;
}
return this.props.responses.map(p => {
return (
<Comment
author={p.author}
avatar={p.avatar}
timeStamp={p.timeStamp}
text={p.text}
/>
);
});
};

How can I grab the key of a list item generated from a map function?

So I am learning React, and I've tried searching for solutions to my problem both on stackoverflow and on React's own documentation, but I am still stumped.
Essentially, I have a list of 10 subreddits that is being mapped to list items in the form of the subredditsArray variable.
I render the results, and try to pass the selected item when I click that list item to my getSubredditInfo function. However, this doesn't work - event.target.key is undefined. (To clarify, I am looking to grab the key of the single list element that I have clicked).
When I try to just get event.target, I get the actual htmlElement (ex: <li>Dota2</li>), where as I want to get the key, or at least this value into a string somehow without the tags. I also tried putting my onClick method in the list tag of the map function, but that did not work.
Here is the relevant code:
//this is where I get my data
componentDidMount(){
fetch('https://www.reddit.com/api/search_reddit_names.json?query=dota2')
.then(results => {
return results.json();
})
.then(redditNames => {
//this is there I set my subreddits state variable to the array of strings
this.setState({subreddits: redditNames.names});
})
}
getSubredditInfo(event){
//console.log(event.target.key); <-- DOESNT WORK
}
render() {
var subredditsArray = this.state.subreddits.map(function(subreddit){
return (<li key={subreddit.toString()}>{subreddit}</li>);
});
return (
<div className="redditResults">
<h1>Top 10 subreddits for that topic</h1>
<ul onClick={this.getSubredditInfo}>{subredditsArray}</ul>
</div>
);
}
My questions essentially boil down to:
How do I grab the key value from my list object?
Additionally, is there a better way to generate the list than I currently am?
Thank you in advance.
EDIT: Added my componentDidMount function in hopes it clarifies things a bit more.
try the following code:
class App extends React.Component {
constructor(props){
super(props);
this.state = {subreddits:[]};
}
componentDidMount(){
fetch('https://www.reddit.com/api/search_reddit_names.json?query=dota2')
.then(results => {
return results.json();
})
.then(redditNames => {
//this is there I set my subreddits state variable to the array of strings
this.setState({subreddits: redditNames.names});
})
}
getSubredditInfo(subreddit){
console.log(subreddit);
}
render() {
return <div className="redditResults">
<h1>Top 10 subreddits for that topic</h1>
<ul>
{
this.state.subreddits.map((subreddit)=>{
return (<li key={subreddit.toString()} onClick={()=>this.getSubredditInfo(subreddit)}>{subreddit}</li>);
})
}
</ul>
</div>;
}
}
ReactDOM.render(
<App/>,
document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>
please check the onClick event handler now. its an arrow function and its calling the getSubredditInfo function with your subreddit now. so you will get it there.
so its basically different way of calling the handler to pass data to the handler.
it works as you expect it to.
You can use lamda function or make component for item list which have own value for getSubredditInfo function
getSubredditInfo(value) {}
render() {
var subredditsArray = this.state
.subreddits.map((subreddit, i) =>
(<li key={i}
onClick={() => this.getSubredditInfo(subreddit)}>{subreddit}</li>));
return (
<div className="redditResults">
<h1>Top 10 subreddits for that topic</h1>
<ul>{subredditsArray}</ul>
</div>
);
}
1) Key should be grabbed either by the id in your object in array. Or you can combine the 2 properties to create a unique key for react to handle re-renders in a better way.
If you have a string array, you may use a combination of string value + index to create a unique value, although using index is not encouraged.
Given a quick example for both below.
2) A better way could be to move your map function into another function and call that function in render function, which will return the required JSX. It will clean your render function.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
subredditsObjArray: [
{ id: 1, value: 'A'},
{ id: 2, value: 'B'},
{ id: 3, value: 'C'},
{ id: 4, value: 'D'}
],
subredditsArray: ['A', 'B', 'C', 'D'],
selectedValue: ''
};
}
getSubredditInfo = (subreddit) => {
console.log(subreddit)
this.setState({
selectedValue: ((subreddit && subreddit.id) ? subreddit.value : subreddit),
});
}
render() {
return (
<div className="redditResults">
<p>Selected Value: {this.state.selectedValue}</p>
<h1>Top {this.state.subredditsArray.length || '0'} subreddits for that topic</h1>
<p>With Objects Array</p>
<ul>
{
this.state.subredditsObjArray
&& this.state.subredditsObjArray.map(redditObj => {
return (<li key={redditObj.id}><button onClick={() => this.getSubredditInfo(redditObj)}>{redditObj.value || 'Not Found'}</button></li>);
})
}
</ul>
<br />
<p>With Strings Array</p>
<ul>
{
this.state.subredditsArray
&& this.state.subredditsArray.map((reddit, index) => {
return (<li key={reddit + '-' + index}><button onClick={() => this.getSubredditInfo(reddit)}>{reddit || 'Not Found'}</button></li>);
})
}
</ul>
</div>
);
}
}
ReactDOM.render(
<App etext="Edit" stext="Save" />,
document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>
Are you trying to do this? I'm not sure what you want to do.
getSubredditInfo(e, subreddit) {
console.log(subreddit)
}
render() {
const { subreddits } = this.state
var subredditsArray = subreddits.map(subreddit => (
<li
key={subreddit.toString()}
onClick={(e) => {
this.getSubredditInfo(e, subreddit)
}}
>
{subreddit}
</li>
))
return (
<div className="redditResults">
<h1>Top 10 subreddits for that topic</h1>
<ul>{subredditsArray}</ul>
</div>
);
}
The key purpose is to pass your subreddit to the onClick function so you will receive the value while you click the item.
If you still get error try this and tell me what's happened.
render() {
const { subreddits } = this.state
var subredditsArray = subreddits.map(subreddit => (
<li
key={subreddit.toString()}
onClick={(e) => {
console.log(subreddit.toString())
}}
>
{subreddit}
</li>
))
return (
<div className="redditResults">
<h1>Top 10 subreddits for that topic</h1>
<ul>{subredditsArray}</ul>
</div>
);
}

TypeError: Cannot read property 'edit' of undefined

I keep getting the following 2 errors for my buttons:
TypeError: Cannot read property 'edit' of undefined
TypeError: Cannot read property 'remove' of undefined
I am building a todo list, each note has 2 buttons 'add' and 'Remove'.
I managed to get the note buttons working when I call DisplayNote once.
Whenever I try to make multiple notes with JS map the buttons stop working and I can't figure out why its not working now. Code is attached.
todo list image
import React from 'react';
class DisplayNote extends React.Component {
handleEdit(e) {
console.log('sdfsdfdfs');
this.props.edit(e)
}
handleRemove(e) {
console.log('sdfsdfdfs');
this.props.remove(e)
}
render(){
return(
<div className="note">
<p>{this.props.note}</p>
<span>
<button onClick={this.handleEdit.bind(this)}>Edit</button>
</span>
<span>
<button onClick={this.handleRemove.bind(this)}>Remove</button>
</span>
</div>
);
}
}
class EditNote extends React.Component {
handleSave(e) {
var val = this.refs.newText.value;
this.props.saveNote(val)
}
render(){
return (
<div className="note">
<textarea ref="newText" defaultValue="test">
</textarea>
<button onClick={this.handleSave.bind(this)}>Save</button>
</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.edit = this.edit.bind(this);
this.saveNote = this.saveNote.bind(this);
this.remove = this.remove.bind(this);
this.state = {
editing: false,
notes: ['Call Tim','sdsdsd', 'dentist', 'Email Julie']
}
}
AppObject = {
count: 1,
price: 15.00,
amount: '12'
}
AppArray = ['tim','ali', 'jim', 'tom']
edit(e) {
this.setState({editing: true});
console.log('AppObject', this.AppObject);
}
saveNote(val) {
this.setState({editing: false});
console.log('Save note value ' + val)
}
remove() {
alert('remove');
console.log('AppArray', this.AppArray);
}
eachNote(note, i) {
return(
<DisplayNote key={i}
note={note}
edit={(e) => this.edit(e)}
remove={(e) => this.remove(e)}>
{note}
</DisplayNote>
);
}
render() {
if(this.state.editing) {
return (
<div>
<EditNote saveNote={(e) => this.saveNote(e)} />
<div>{this.props.count}</div>
</div>
);
}else{
return (
<div>
/* Calling it once*/<DisplayNote edit={(e) => this.edit(e)} remove={(e) => this.remove(e)} />
<div>{this.props.count}</div>
<div>
/* Using map to create multiple notes */{this.state.notes.map(this.eachNote)}
</div>
</div>
);
}
}
}
App.propTypes = {
count: function(props, propName){
if(typeof props[propName] !== 'number'){
return new Error('Count prop must be a number');
}
if(props[propName] > 100){
return new Error('Creating ' + props[propName] + ' notes is too much!');
}
}
}
export default App;
I think you are loosing the context inside map function, you need to define the binding for that also.
Use this line in the constructor, it will bind that function:
this.eachNote = this.eachNote.bind(this);
Or use that function like this:
{this.state.notes.map((note, i) => this.eachNote(note,i)}
Or
{this.state.notes.map(this.eachNote)}
eachNote = (note, i) => { //use arrow function here
}

How do I loop through an array in an array of objects

I know how to run loops inside react but how do I do it inside an object which is already inside an array being looped?
I am trying to display each ingredient item as an <li>, so far I have got it working with recipe but I am lost with ingredient. If anyone could chime in, I'd appreciate it.
var Recipes = React.createClass({
// hook up data model
getInitialState: function() {
return {
recipeList: [
{recipe: 'Cookies', ingredients: ['Flour ', 'Chocolate']},
{recipe: 'Cake', ingredients: ['Flour ', 'Sprinkles']},
{recipe: 'Onion Pie', ingredients: ['Onion ', 'Pie-Crust']}
]
}
},
loop: function() {
{this.state.recipeList.flatMap('ingredients').map(item, index) => (
<li key={index} className="list-group-item">{ingredient.ingredients}</li>
)}
},
render: function() {
return (
<div>
{this.state.recipeList.map((item, index) => (
<div className="panel panel-default">
<div className="panel-heading"><h3 className="panel-title">{item.recipe}</h3></div>
<div className="panel-body">
<ul className="list-group">
{this.loop}
</ul>
</div>
</div>
)
)}
</div>
);
}
});
How about this way :
loop: function(ingredients) {
return ingredients.map((ingredient, index) => {
return (<li key={index} className="list-group-item">{ingredient}</li>)
})
},
render(){
...
{this.loop(item.ingredients)}
...
}
One more thing, you shouldn't use index of array as key because it will be difficult to manage when editting the array later. It will be better if you assign key with something very unique like id or index + Date.now()
You seem to be missing a return statement in the loop method.
You can cascade rendering as deep as you'd wish, only remember that you need to call the method instead of just placing it in the component structure (see this.loop without call parentheses in your sample):
var myComponent = React.createClass({
renderListElements: function (parent) {
return this.state.listElements[parent].map((element, index) => (
<li
className="my-component__sub-component__list-element"
key={`${parent.uid}_${element.uid}`}
>
{element.name}
</li>
));
},
render: function () {
var parentsId = [ 0, 1, 2, 3 ];
return (
<div className="my-component">
{parentsId.map((item, index) => (
<div
className="my-component__sub-component"
key={item.uid}
>
{this.renderListElements(item)}
</div>
)}
<div/>
);
}
});

Categories

Resources