React mapping fetched data - json

I'm trying to fetch some data from my database with some simple to-dos. However I cant seem to map them out into a list on my site.
I keep getting errors like: todoFromServer.map is not a function or that todoFromServer is not an array etc.
My current code looks like this:
import apiFacade from "../api/apiFacade";
import React, { useState, useEffect } from "react";
import {Form, FormGroup, Label, Input, Button} from "reactstrap"
export default function SecurePage() {
const [todoFromServer, setTodoFromServer] = useState("Waiting...");
useEffect(() => {
apiFacade.getTodo().then((data) => setTodoFromServer(data));
}, []);
return (
<div className="container-fluid padding">
<div className="row">
<div className="col-3"></div>
<div className="col-6 text-center">
<Form>
<FormGroup>
<h3 className="mt-5">Todos</h3>
<Input type="text" placeholder="Enter Todo"></Input>
</FormGroup>
<Button type="submit">Add</Button>
</Form>
<div>
{todoFromServer.map(() => (
<div>{todoFromServer.todoText}</div>
))}
</div>
</div>
</div>
</div>
);
}
The data I trying to fetch should come out as json looking like this:
I'm kind of lost.. Hope someone can help me out
to be clear - I want the data mapped out on a list with a delete button next to it...

const [todoFromServer, setTodoFromServer] = useState([]); // <=== initialize this as an empty array.
useEffect(() => {
apiFacade.getTodo().then((data) => setTodoFromServer(data)); // Make sure data returned from Promise resolve is indeed an array
}, []);
You want to read todoText of each todo's inside your array item so you would do something like this.
{todoFromServer.length ? todoFromServer.map((todo) => (
<div>{todo.todoText}</div>
)) : "Waiting..."}
For additional reference, take a look at Array.map usage here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map

Related

How to only rerender a new element in an array - React

I have a web app that uses a websocket to receive information from an API I have put together.
Everything works great, however, every time new information arrives from the websocket, the whole list on my frontend (React) is updated.
Here is the relevent code:
componentDidMount(prevState) {
socketIO.on('newNotification', (response) => {
const notifications = this.state.notifications;
console.log(response)
const newNotifications = response.data
this.setState(prevState => ({
notifications: [...this.state.notifications, newNotifications]
}))
});
}
notifications is a list of notifications that is received from my API, which I set to the state.notifications whenever a response is received.
My understanding is React only updates what it needs to, so I'm not sure what is going on.
Here is my Notification component:
import React from "react"
class Notification extends React.Component {
render(){
return(
<ul>
<li
key = {this.props.notification.id}
onClick={() => this.props.deleteNotificationProps(this.props.notification.id)}>
<div className='separator-container'>
<div className={'notification-border ' + this.props.notification.stat_abr}>
<div className='notification' >
<div className='left-notification'>
<div className = 'stat-abr'>{this.props.notification.stat_abr}</div>
<div className = 'game-time'>{this.props.notification.game_time_string}</div>
</div>
<div className='middle-notification'>
<div className='player-image'>
<img src={"http://nhl.bamcontent.com/images/headshots/current/168x168/" + this.props.notification.player_id.toString() + ".jpg"} alt="" className="player-img" />
</div>
</div>
<div className = 'right-notification'> {this.props.notification.description} </div>
</div>
</div>
</div>
</li>
</ul>
)
}
}
export default Notification
I tried various diferent methods of updating the state, but nothing seems to work.
EDIT: here is the NotificationList class where the Notification component is created:
class NotificationList extends React.Component {
render() {
return(
<ul>
{this.props.notifications.map(notification => (
<Notification
id = {notification.id}
notification = {notification}
handleChangeProps = {this.props.handleChangeProps}
deleteNotificationProps = {this.props.deleteNotificationProps}
/>
))}
</ul>
)
}
}
I can't see the code where you iterate over the notifications to create Notification components.
I assume you have a notifications.map(...) somewhere... To only re-render new components, use the key={...} attribute inside of the map, with a value unique to each attribute (use index if you don't have a unique key).
e.g.
<div>
{ notifications.map((notification) => <Notification
key={notification.id}
notification={notification}
/>)
}
</div>
Figured out what was wrong:
I had a <ul></ul> tag surrounding my <li></li> tag in my Notification class.
Removed this and all is working as it should.

React adding favorites with checkboxes

Objects
Favourites
im trying to do a website witch react and i use an api to recieve data. The Data i recieved gets put into a list and then i produce a button for every item in this list. Now i also produce a check box for every item in the list, but the production is in a seperate component. what i want to do ist that, if the checkbox of one item gets checked, the item should be stored in a cache and put out again as an button on a seperate page. My Question now is how do i do that?
Thank you in advance.
This is where i produce the checkbox:
import React from "react";
export default function Favcheck() {
return (
<>
<div class="favcheck">
Favorit
<input type="checkbox" name="name" class="checkbox" id="heart" />
</div>
</>
);
}
this is where the buttons are made:
import axios from "axios";
import * as React from "react";
import Favcheck from "./favcheck.jsx";
import Mensapage from "./mensapage.jsx";
import site from "./home.jsx";
export default function Mensbuttons(props) {
return (
<>
<div class="formcontainer">
<form method="get" action="/mensapage" id="mensaform">
<button type="submit" class="mensabutton" key={props.key}>
<div class="mensatext">{props.name}</div>
</button>
<br></br>
<Favcheck />
</form>
</div>
</>
);
}
and this is where the buttons are used:
import React,{ useState, useEffect } from "react";
import axios from 'axios';
import Nav from "./nav.jsx";
import Mensbuttons from "./mensbuttons.jsx";
export default function Home(props) {
let site="test";
const[posts,setPosts] = useState([])
useEffect(()=>{
axios.get('https://openmensa.org/api/v2/canteens?near[lat]=52.517037&near[lng]=13.38886&near[dist]=15')
.then(res =>{
setPosts(res.data)
})
.catch(err =>{
console.log(err)
})
},[])
console.log(posts);
return (
<>
<Nav />
<div class="header">
<h1>Mensen</h1>
</div>
{posts.map((list) => {
return <Mensbuttons name={list.name} key={list.id} />;
})}
</>
);
}
here are some mockup pictures
i want to get specific objects to the favourites page by checking the checkbox
here are the favourites
here are the buttons with checkboxes

React JSON - Display data in a Card Slider

I have a local JSON file which I've converted to Javascript.
I am able to fetch the data by importing the JS file into my App.js.
This is my App.js file:
import React, { Component } from "react";
import CardData from "./data/db";
import "./App.css";
class App extends Component {
constructor() {
super();
this.state = {
CardData
};
}
render() {
return (
<div>
{this.state.CardData.map(cards => (
<div className="card">
<span>{cards.title}</span>
<br />
<span>{cards.subtitle}</span>
<br />
</div>
))}
</div>
);
}
}
export default App;
I want to be able to show 3 Cards, and then have the option to slide across to the remaining cards.
Something like this
However I am only able to show it in one div, is there a way to do it in the way I've called the JSON or is there a way to separate the JSON data by their ID?
Since you are looking for a simpler way to achieve the same result I would suggest switching your App to a stateless component, as it is never updating/using any state value :
import React from "react";
import CardData from "./data/db";
import "./App.css";
const App = props => (
<React.Fragment> //A fragment will not appear in your DOM
{CardData.map(({ title, subtitle }, index) => ( //Deconstructs each cards
<div className="card" key={index}>
<span>{title}</span>
<br />
<span>{subtitle}</span>
<br />
</div>
))}
</React.Fragment>
)
export default App;
But this component will never be able to render anything else than this specific JSON file, if you want it to be more generic, you should send your data via the component's props :
import React from "react";
import "./App.css";
const App = ({ cards }) => (
<React.Fragment>
{cards.map(({ title, subtitle }, index) => (
<div className="card" key={index}>
<span>{title}</span>
<br />
<span>{subtitle}</span>
<br />
</div>
))}
</React.Fragment>
)
export default App;
And in your parent component :
import CardData from "./data/db";
const Parent = props => <App cards={CardData}/>
You should also not forget about keys when mapping elements, as every mapped component should have a unique and persistent key.

REACT - Image gallery

I'm retrieving images from the database in REACT and have created a holder for an image with thumbnails at the bottom.
I would like to know how I can make the interface behave like eCom sites, whereupon clicking the thumbnail, its respective image is loaded in the bigger area.
Below is the REACT code.
import React from "react";
import { Link } from "react-router-dom";
import ImageList from "../ImageList";
const ProductDetails = props => {
const images = require.context(
"../../../strapui/app/public/uploads",
true,
/\.jpg$/
);
const keys = images.keys();
const svgsArray = keys.map(key => images(key));
return(
<div className="desContainer ">
<div className="desimgContainer ">
<ImageList
styles="heroImage"
imagePath={props.selectedItem[0].image[0]}
svgsArray={svgsArray}
/>
</div>
<div className="thumbs">
<ImageList
styles="thumbnail"
imagePath={props.selectedItem[0].image[0]}
svgsArray={svgsArray}
/>
</div>
<div className="thumbs">
<ImageList
styles="thumbnail"
imagePath={props.selectedItem[0].image[1]}
svgsArray={svgsArray}
/>
</div>
<div className="thumbs">
<ImageList
styles="thumbnail"
imagePath={props.selectedItem[0].image[2]}
svgsArray={svgsArray}
/>
</div>
</div>
);
};
export default ProductDetails;
The images are pulled from the database using the following code
import React from "react";
const ImageList = props => {
if (
props.imagePath === undefined ||
props.imagePath === null ||
props.imagePath.length === 0
)
return null;
const path = props.svgsArray.find(
str => str.indexOf(props.imagePath.hash) > 1
);
return <img src={path} alt={props.imagePath.hash} className={props.styles} />;
};
export default ImageList;
I was wondering if I could use a switch case to show the image when a thumbnail is clicked?
will it work? if it will, can you pls direct me how?
Use onClick event and attach it with some function which should do some code magic.
for e.g:
largeSizeImage () {
/* some code logic */
}
return (
<div className="thumbs" onClick={largeSizeImage()}>
<ImageList
styles="thumbnail"
imagePath={props.selectedItem[0].image[1]}
svgsArray={svgsArray}
/>
</div>
)

React: Create a new html element on click

I've used React for a couple of weeks now but I have this simple problem that I can't seem to wrap my head around. It's about creating new html elements.
I would just like to know in general if the way that I went about it, is the "right way" or is there another preferred way to create new html element with a click function.
For some reason this problem took awhile for me to figure out and it still feels a bit strange, that's why I'm asking.
Thanks in advance!
import React, { Component } from 'react';
import './Overview.css';
import Project from './Project';
class Overview extends Component {
constructor() {
super()
this.state = {
itemArray: []
}
}
createProject() {
const item = this.state.itemArray;
item.push(
<div>
<h2>Title</h2>
<p>text</p>
</div>
)
this.setState({itemArray: item})
//console.log(this.state)
}
render() {
return (
<div className="Overview">
<p>Overview</p>
<button onClick={this.createProject.bind(this)}>New Project</button>
<Project />
<div>
{this.state.itemArray.map((item, index) => {
return <div className="box" key={index}>{item}</div>
})}
</div>
</div>
);
}
}
export default Overview;
No, this is not a correct approach. You shouldn't be generating HTML elements like that, nor keep them in state - it is against React to manipulate DOM like that. You won't be able to utilize Virtual DOM is the first thing that I can think of.
What you should do instead is keep all data that is needed for rendering in state and then generate the HTML element from there, for instance
createProject() {
const item = this.state.itemArray;
const title = '';
const text = '';
item.push({ title, text })
this.setState({itemArray: item})
}
render() {
return (
<div className="Overview">
<p>Overview</p>
<button onClick={this.createProject.bind(this)}>New Project</button>
<Project />
<div>
{this.state.itemArray.map((item, index) => {
return (
<div className="box" key={index}>
<div>
<h2>{item.title}</h2>
<p>{item.text}</p>
</div>
</div>
)
})}
</div>
</div>
);
}