JSX Not Appearing in Map Function [duplicate] - javascript

This question already has answers here:
When should I use a return statement in ES6 arrow functions
(6 answers)
Closed 17 hours ago.
I want to print the contents of an array of objects. I'm trying to render JSX from a component that gets the array as a prop, maps through an array of objects, and prints the content to the screen. The map is iterating however I'm getting no output. What am I missing? I'm getting no errors regarding either component.
export default function Games(){
const db = [
{
game: "A Random Game",
reviewer: "Random Reviewer",
review: "This is a random review"
},
{
game: "A Random Game Two",
reviewer: "Random Reviewer Two",
review: "This is a random review Two"
},
]
return (
<Articles db = {db} />
)
}
const Articles = (props) =>{
{props.db.map((item)=>{
return <h1 key={item.game}>{item.game}</h1>
})}
}

Directly return the value of map in the Articles component:
const Articles = (props) =>{
return props.db.map(item => <h1 key={item.game}>{item.game}</h1>);
}

Related

Iterating objects in React JS

What is the Error in the below code? I tried to iterate over the array of objects using the map. but I am receiving an error. Please find attached the code for reference.
import "./styles.css";
export default function App() {
const sample = [{
samplePath : "/website",
par:{
abc:"123",
def: "678",
ghi:"456"}
}];
const createURL = (re)=> {
const q = Object.entries(re.par)
.map(([key, value]) => key +'='+value).join("&");
return "?"+ q;
}
console.log(createURL(sample));
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
I am receiving the following error
You're trying to call Object.entries on re.par. In this case, re is an array, so calling .par on it returns undefined. I'm not sure what your desired result is, but if you access the first element in the array and then .par, maybe that's what you're after?
const sample = [{
samplePath : "/website",
par:{
abc:"123",
def: "678",
ghi:"456"}
}];
const createURL = (re)=> {
const q = Object.entries(re.par)
.map(([key, value]) => key +'='+value).join("&");
return "?"+ q;
}
console.log(createURL(sample[0]));
In your example sample is an array, containing one object.
So, either, remove the [] such that sample is an object like you are referring to it in your code. Or, Object.entries(re[0].par)

Trying to build React Component that can iterate over a nested data structure and produce styled HTML elements based on type

I have a nested data structure I'm getting back from an API that contains sections of text as objects inside of an array.
I'm trying to iterate over the initial array of sections, check to see what type the section is, and then based on the type iterate over the copy array to style and render each string as the appropriate HTML element.
The problem I'm running into is that I'm trying to accomplish this by using map and then a switch statement and then another map for each copy section and nothing is rendering.
Here's a CodeSandbox I created as a mock up of the problem
This is my current component:
import React from "react";
import styled from "styled-components";
function renderElement(sections) {
if (sections) {
sections.map((section) => {
switch (section.textType) {
case "title":
return section.copy.map((text) => <Title>{text}</Title>);
case "subtitle":
return section.copy.map((text) => <Subtitle>{text}</Subtitle>);
case "body":
return section.copy.map((text) => <Body>{text}</Body>);
default:
return section.copy;
}
});
}
}
const TextComponent = (props) => {
const { sections } = props;
return <>{renderElement(sections)}</>;
};
export default TextComponent;
const Title = styled.h1`
font-size: 28px;
`;
const Subtitle = styled.h4`
font-size: 22px;
`;
const Body = styled.p`
font-size: 16px;
`;
And this is the data structure:
const data = {
sections: [
{
textType: "title",
copy: ["Doctor Strange"]
},
{
textType: "subtitle",
copy: ["As Earth’s Sorcerer Supreme, Doctor Strange wields arcane spells and mystical artifacts to defend the planet against malevolent threats.", "The one who protects your reality"]
},
{
textType: "body",
copy: [
"Recognized the world over as one of the most brilliant neurosurgeons, Stephen Strange’s arrogance and self-centered nature grew alongside his skill until his ego far outweighed his career.",
"Knowing his reliance on his medical abilities to support his affluent lifestyle, Strange began to seek a source of healing for his hands until the quest drained him of his resources and he faced a dark and uncertain future."
]
}
]
}
Replace "text" with the copy array and map the content to p tags similar to what you did on the body tag
Figured out what I was doing wrong and was able to display all elements correctly by pushing them to a second array within my function and returning that new array like so:
function renderElement(sections) {
const elements = []
if (sections) {
sections.map((section) => {
switch (section.textType) {
case "title":
return elements.push(section.copy.map((string) => <Title>{string}</Title>));
case "subtitle":
return elements.push(section.copy.map((string) => <Subtitle>{string}</Subtitle>));
case "body":
return elements.push(section.copy.map((string) => <Body>{string}</Body>));
default:
return section.copy;
}
});
}
return elements
}

Update array state in functional components using React [duplicate]

This question already has answers here:
The useState set method is not reflecting a change immediately
(15 answers)
Closed 2 years ago.
I'm trying to update the cars array whenever the Add button is clicked. I see that new object is added to updatedCars array but when I try to set the state with it, the cars state won't get updated.
I still see the initial state even after adding a new object in the array.
export default function App() {
const [cars, setCars] = useState([{ name: "Audi", type: "sedan" }]);
const handleAdd = () => {
const newCar = { name: "Benz", type: "sedan" };
const updatedCars = [...cars, newCar];
console.log("updatedCars", updatedCars);
setCars(updatedCars);
console.log("result", cars);
};
return (
<div className="App">
<button onClick={handleAdd}>Add</button>
</div>
);
}
I created a working example using CodeSandbox. Could anyone please help?
setCars is the asynchronous method, and you can't get the updated value of cars immediately after setCars()
setCars(updatedCars);
console.log(cars); //This will console old value of `cars`
You should use useEffect with adding a cars dependency to check updated value.
useEffect(() => {
console.log(cars);
}, [cars]);

How can I pull a specific object from the state and get one of its items?

I have the following component named Tweets.js:
import React, {Component} from "react";
export default class Tweets extends Component {
constructor(props) {
super(props);
this.state = {tweets: [], users: []};
}
componentDidMount() {
this.TweetList();
this.UserList();
}
TweetList() {
fetch('http://***.***.***.***:****/api/posts')
.then(res => res.json())
.then((data) => {
this.setState({ tweets: data })
})
.catch(console.log);
}
UserList() {
fetch('http://***.***.***.***:****/api/users')
.then(res => res.json())
.then((data) => {
this.setState({ users: data })
})
.catch(console.log);
}
render() {
const tweets = this.state.tweets.map((item, i) => (
<div>
<div class="tweet_box">
<p>{item.tweet}</p>
</div>
</div>
));
const users = this.state.users.map((item, i) => (
<div>
<h2>{item.twitterhandle}</h2>
</div>
))
return (
<div className="tweet">{ tweets }</div>
)
}
}
users json:
[
{
"userid": 337282,
"twitterhandle": "sean",
"email": "sean#sean.com"
},
{
"userid": 1234563,
"twitterhandle": "DidItWork",
"email": "pleasework#work.com"
},
{
"userid": 3728,
"twitterhandle": "Isthisup",
"email": "isthisup#up.com"
},
{
"userid": 1234567,
"twitterhandle": "blah",
"email": "matt#knop.com"
}
]
posts json:
[
{
"postid": 2,
"tweet": "Hello, World #2",
"userid_id": 337282
},
{
"postid": 4,
"tweet": "Hello, World #1",
"userid_id": 3728
},
{
"postid": 1,
"tweet": "Hello, World #4",
"userid_id": 3728
},
{
"postid": 3,
"tweet": " Hello, World! How's it?",
"userid_id": 1234563
}
]
I am trying to return the list of Posts from the posts json file and I was able to accomplish that. My question is, how am I able to take the userid_id number, find it in the users json file, pull the username, and display it inside the following code?
const tweets = this.state.tweets.map((item, i) =>
<div>
<div class="tweet_box">
<p>{item.tweet}</p>
</div>
</div>
));
All I have been able to accomplish is to get it to display a separate list of the users but I can't figure out how to route the data into the above code - in say, an h1 tag.
I am very new to react and I apologize if my code is terrible. I am open to any and all suggestions or reference I can look at. I looked through the React documentation and found the section on keys and it appeared to be the correct direction, but still couldn't figure it out.
If there is anything I can add to clarify my issue, please don't hesitate to ask.
I appreciate all of your time.
EDIT: I am attempting to work through the problem some more and added the following function:
GetUsername() {
return this.state.users[1];
}
I then made the following changes to my render:
const tweets = this.state.tweets.map((item, i) => (
<div>
<div class="tweet_box">
<h1>{this.GetUsername()}</h1>
<p>{item.tweet}</p>
</div>
</div>
));
I added
<h1>{this.GetUsername()}</h1>
Which produced the following error:
Error: Objects are not valid as a React child (found: object with keys
{userid, twitterhandle, email, password}). If you meant to render a
collection of children, use an array instead.
You are close. React doesn't render objects directly but can render primitives, like strings and numbers. this.state.users[1] will always return the second user object in the users array, which probably isn't what you want.
Update your utility to take an id and find the correct user object.
getUserById = (id) => this.state.users.find(({ userid }) => userid === id);
Then call that in the render to get the user object and if found access the twitterhandle property.
const tweets = this.state.tweets.map((item, i) => (
<div>
<div class="tweet_box">
<h1>{this.getUserById(item.userid_id)?.twitterhandle}</h1>
<p>{item.tweet}</p>
</div>
</div>
));
This GetUserName method returns an object (the user's object). It cannot render an object, but it can render a string or a number. for example:
GetUsername() {
return this.state.users[1].twitterhandle;
}
Also you can return a array and iterate with a map like you do in the const "tweet"
The error you are getting, "Objects are not valid as a React child", is because your GetUsername function actually returns the whole user object instead of just the username. You need to change this.state.users[1] to this.state.users[1].twitterhandle.
But we want to find the right user for each tweet, not just return the first user all the time. We do this by looking through the users array and finding the element where user.userid matches tweet.userid_id.
Let's make a getUsername function that takes the numeric user id as an argument:
getUsername( id ) {
const user = this.state.users.find( user => user.id === id );
if ( user ) {
return user.twitterhandle;
} else {
return ''; // fallback to return an empty string if no user was found
}
}
(You can write that a lot more concisely, but I wanted to be clear about the intention)
Now we can call that with the id from the tweet item
const tweets = this.state.tweets.map((item, i) => (
<div>
<div class="tweet_box">
<h1>{this.getUsername(item.userid_id)}</h1>
<p>{item.tweet}</p>
</div>
</div>
));

Remove items from an array React?

I am making a form that allows a user to build a quiz (here's my code so far)
var uuid = require("uuid-v4");
// Generate a new UUID
var myUUID = uuid();
// Validate a UUID as proper V4 format
uuid.isUUID(myUUID); // true
var questionNum = 0;
class App extends Component {
constructor(props) {
super(props);
this.state = {
key: uuid(),
title: "",
author: "",
questions: [],
answers: []
};
this.handleChange = this.handleChange.bind(this);
this.addQuestion = this.addQuestion.bind(this);
}
componentDidMount() {
// componentDidMount() is a React lifecycle method
this.addQuestion();
}
handleChange(event) {
const target = event.target;
const value = target.type === "checkbox" ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
/**
* It's probably better to structure your questions like this:
* this.state.questions: [{
* question: 'How are you?',
* answers: ['Good', 'Great', 'Awful'],
* // correctAnswer: 'Great'
* },
* {
* question: 'What is your name?',
* answers: ['Toby', 'Marco', 'Jeff'],
* // correctAnswer: 'Jeff'
* }];
*
* This allows you to keep better track of what questions
* have what answers. If you're making a 'quiz' type structure,
* you could additionally add a `correctAnswer` property.
*/
addQuestion() {
questionNum++;
this.setState(previousState => {
const questions = [...previousState.questions, "question", "hi"];
const answers = [...previousState.answers];
for (var i = 0; i < 4; i++) {
answers.push({
answerChoice: "",
key: uuid()
});
}
return { questions, answers };
});
console.log(
this.state.answers,
this.state.questions,
questionNum,
this.state.title,
this.state.author
);
}
render() {
return (
<div className="App">
<div>
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Quiz Form 2.0</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
</div>
<div>
<form>
<div className="Intro">
Give your Quiz a title:{" "}
<input
type="text"
value={this.state.title}
onChange={this.handleChange}
name="title"
/>
<br />
Who's the Author?{" "}
<input
type="text"
value={this.state.author}
onChange={this.handleChange}
name="author"
/>
<br />
<br />
</div>
<div className="questions">
Now let's add some questions... <br />
{// This is where we loop through our questions to
// add them to the DOM.
this.state.questions.map(question => {
return <div>{question}</div>;
})
// This is what it would look like for the structure
// I proposed earlier.
// this.state.questions.map((question) {
// return (
// <div>{question.quesion}</div>
// {
// question.answers.map((answer) => {
// return (<div>{answer}</div>);
// })
// }
// );
// })
// This would output all questions and answers.
}
</div>
</form>
<button onClick={this.addQuestion}>Add Question</button>
</div>
</div>
);
}
}
export default App;
And I have come to the point where I want to try and be able to "Remove" a question (using a button). What my code does now is it adds objects to an array, and I have got that figured out. But now I am trying to remove Items from the array. I was thinking "ok just remove the last question" but realistically users will want to remove any of their questions. I was just curious if anyone had some tips for this, I really don't know how to start.
If you want the user to be able to remove any question, add an onClick to the question div (or a child of the div - remember to move the onClick). The callback for that can accept an index, which refers to the element in the list to remove.
Example:
class App extends Component {
constructor(props) {
super(props)
this.removeItem = this.removeItem.bind(this)
}
removeItem (index) {
this.setState(({ questions }) => {
const mQuestions = [ ...questions ]
mQuestions.splice(index, 1)
return { questions: mQuestions }
})
}
render () {
return (
<div>
...
{ this.state.questions.map((question, index) => {
return <div onClick={ () => this.removeItem(index) }>{ question }</div>
}) }
</div>
)
}
}
This isn't so much a react question as a JavaScript question. Since your questions are stored in your react state it will update the DOM when that state is modified. Simply removing the value from the array using this.setState() will suffice.
You have a few options for removing values from your array. The main thing to keep in mind here is to make sure you don't modify the actual array but rather replace it with a new instance of an array. Modifying the array directly will not trigger updates and goes against the general principles of react state. For example, using
Array.prototype.splice() would modify your original array.
(Array.prototype.splice docs)
In JavaScript, primatives such as strings and numbers are passed by value however objects such as arrays, sets, or generic JavaScript objects are passed by reference. This means that by assigning an object to a new variable you will now have two variables pointing at the same object.
const foo = [1];
const bar = foo;
console.log(foo); // [1]
console.log(bar); // [1]
foo.push(2);
console.log(foo); // [1, 2]
console.log(bar); // [1, 2]
One common way to get around this is by using the ES6 spread notation (Spread operator docs) to spread the values into a new array. const bar = [...foo] would return a copied array pointing to a separate object. Applying this to your question, you could do const q = [...this.state.questions] and then modify q using q.splice(index, 1) and assign that to your state using this.setState(). There are obviously other options to removing items from an array and I suppose it is not a given that you know the index of the array. In this case, tools such as Array.prototype.find() or Array.prototype.findIndex() are helpful or you could use a JavaScript Map object (Map docs) in place of your array to eliminate the need for indexes while maintaining question order. All of these options are equally valid so I'll leave it to you to determine how you want to go about it.
In order to actually trigger the deletion you will need to have some sort of user control on the page. You can do this with click listeners on elements, specific buttons included in the HTML for each question, or maybe even a dropdown menu elsewhere for deletion. The end result will be that the element the user interacts with will maintain a unique ID so that when it activates the callback function you can determine which question it is that should be deleted. This ID will be stored in the first argument of your function. In most examples this will be named "event".

Categories

Resources