How to set local JSON data into functional components in React? - reactjs

I'm new to react. I got stucked here. I'm not sure how to pass json data that is getting returned as function to useState.I used classes and everything worked perfectly fine. Now i'm trying to convert that code into functional components. When I delete an item it displays an error. movie.filter is not a function.
index.js
import React, { Component,useState } from 'react'
import {getMovies} from "../services/fakeMovieService"
function Movies() {
const movies = getMovies()
const [movie, setMovie] = useState(movies);
const handleDelete = (movie) => {
const newM= movie.filter(m => m._id != movie._id)
setMovie({newM})
}
return (
<React.Fragment>
<table className="table">
<thead>
<tr>
<th>Title</th>
</tr>
</thead>
<tbody>
{movie.map(movie =>(
<tr key={movie._id}>
<td>{movie.title}</td>
<td>{movie.genre.name}</td>
<td>{movie.numberInStock}</td>
<td>{movie.dailyRentalRate}</td>
<td><button onClick={()=>handleDelete(movie)} className="btn btn-danger btn-sm">Delete</button></td>
</tr>
))
}
</tbody>
</table>
</React.Fragment>
);
}
export default Movies;
JSON
import * as genresAPI from "./fakeGenreService";
const movies = [
{
_id: "5b21ca3eeb7f6fbccd471815",
title: "Terminator",
genre: { _id: "5b21ca3eeb7f6fbccd471818", name: "Action" },
numberInStock: 6,
dailyRentalRate: 2.5,
publishDate: "2018-01-03T19:04:28.809Z"
},
{
_id: "5b21ca3eeb7f6fbccd471816",
title: "Die Hard",
genre: { _id: "5b21ca3eeb7f6fbccd471818", name: "Action" },
numberInStock: 5,
dailyRentalRate: 2.5
},
{
_id: "5b21ca3eeb7f6fbccd471817",
title: "Get Out",
genre: { _id: "5b21ca3eeb7f6fbccd471820", name: "Thriller" },
numberInStock: 8,
dailyRentalRate: 3.5
},
{
_id: "5b21ca3eeb7f6fbccd471819",
title: "Trip to Italy",
genre: { _id: "5b21ca3eeb7f6fbccd471814", name: "Comedy" },
numberInStock: 7,
dailyRentalRate: 3.5
},
{
_id: "5b21ca3eeb7f6fbccd47181a",
title: "Airplane",
genre: { _id: "5b21ca3eeb7f6fbccd471814", name: "Comedy" },
numberInStock: 7,
dailyRentalRate: 3.5
},
{
_id: "5b21ca3eeb7f6fbccd47181b",
title: "Wedding Crashers",
genre: { _id: "5b21ca3eeb7f6fbccd471814", name: "Comedy" },
numberInStock: 7,
dailyRentalRate: 3.5
},
{
_id: "5b21ca3eeb7f6fbccd47181e",
title: "Gone Girl",
genre: { _id: "5b21ca3eeb7f6fbccd471820", name: "Thriller" },
numberInStock: 7,
dailyRentalRate: 4.5
},
{
_id: "5b21ca3eeb7f6fbccd47181f",
title: "The Sixth Sense",
genre: { _id: "5b21ca3eeb7f6fbccd471820", name: "Thriller" },
numberInStock: 4,
dailyRentalRate: 3.5
},
{
_id: "5b21ca3eeb7f6fbccd471821",
title: "The Avengers",
genre: { _id: "5b21ca3eeb7f6fbccd471818", name: "Action" },
numberInStock: 7,
dailyRentalRate: 3.5
}
];
export function getMovies() {
return movies;
}

setMovie({newM})
should be
setMovie(newM)
because your state is an array. The argument movie and state movie have the same name so you're trying to use Array.prototype.filter on an object.
Rename the restructured array values of useState to movies and setMovies:
const initialMovies = getMovies()
const [movies, setMovies] = useState(initialMovies);
Use functional state update as the new state depends on the old state:
const handleDelete = (movie) => {
setMovies(previousMovies => previousMovies.filter(m => m._id !== movie._id))
}
and use movies to render
{movies.map(movie => (...

Related

How to pass in the initial state of the component

I have a React component and I want to test it with React Testing Library. The component has an initial state but I want to override it for my tests and provide a custom initial state so I can test various things. How to I do that in Jest?
function App() {
const initialState = [
{
id: uuidv4(),
date: new Date(),
desc: "initial",
amount: 0,
type: "travel",
},
];
const [expenses, setExpenses] = useState(initialState);
return (
<div className={Styles.app}>
<h1 className="p-4">Expense tracker</h1>
<Container>
<AddExpense setExpenses={setExpenses} expenses={expenses} />
<FilterExpense expenses={expenses} setExpenses={setExpenses} />
<ListExpense setExpenses={setExpenses} expenses={expenses} />
<BreakdownExpense expenses={expenses} />
</Container>
</div>
);
}
I tried the following but it's not working:
test("custom initial state", () => {
const initialState = [
{
id: 1,
date: new Date(),
desc: "test 1",
amount: 0,
type: "10",
},
{
id: 2,
date: new Date(),
desc: "test 2",
amount: 0,
type: "20",
},
{
id: 3,
date: new Date(),
desc: "test 3",
amount: 0,
type: "30",
},
];
const [expenses, setExpenses] = React.useState(initialState);
render(<App expenses={expenses} />);
expect(screen.getByText(/test 1/i)).toBeInTheDocument();
expect(screen.getByText(/test 2/i)).toBeInTheDocument();
});
--- Update
I have a fix for it but I don't like the implementation. I'm putting testing data and logic in the component but I should be keeping them separate.
App comp:
function App({ testApp }) {
const testInitialState = [
{
id: 1,
date: new Date(),
desc: "test 1",
amount: 0,
type: "10",
},
{
id: 2,
date: new Date(),
desc: "test 2",
amount: 0,
type: "20",
},
{
id: 3,
date: new Date(),
desc: "test 3",
amount: 0,
type: "30",
},
];
const initialState = [
{
id: uuidv4(),
date: new Date(),
desc: "initial",
amount: 0,
type: "initial",
},
];
const [expenses, setExpenses] = useState(
testApp ? testInitialState : initialState
);
return (......
test.js:
test(("filter expenses buttons", () => {
render(<App testApp={true} />);
expect(screen.getByText(/test 1/i)).toBeInTheDocument();
expect(screen.getByText(/test 2/i)).toBeInTheDocument();
});

How do i map through array to get array of object?

I have an array called data. How do i extract sub_data? Just need the sub_data part for each object.
const data = [
{
id: 1,
title: 'Logo'
sub_data: [
{
id: 2,
title: 'Company Logo'
},
{
id: 3,
title: 'Website Logo'
},
]
},
{
id: 2,
title: 'Brands'
sub_data: [
{
id: 25,
title: 'Company Brands'
},
{
id: 3,
title: 'Website Brands'
},
]
}
]
Example output will get two outputs because there is 2 objects:
const subData = [
{
id: 2,
title: 'Company Logo'
},
{
id: 3,
title: 'Website Logo'
},
]
const subData = [
{
id: 25,
title: 'Company Brands'
},
{
id: 3,
title: 'Website Brands'
},
]
Not very sure how to use the map function just to get sub_data in the correct structure
You can use flatMap to get sub_data in one array
const data = [
{
id: 1,
title: 'Logo',
sub_data: [
{
id: 2,
title: 'Company Logo'
},
{
id: 3,
title: 'Website Logo'
},
]
},
{
id: 2,
title: 'Brands',
sub_data: [
{
id: 25,
title: 'Company Brands'
},
{
id: 3,
title: 'Website Brands'
},
]
}
]
const result = data.flatMap(item => item.sub_data)
console.log(result)
If you want an array with the sub_data objects you can just map the original array:
const data = [
{
id: 1,
title: 'Logo',
'sub_data'
: [
{
id: 2,
title: 'Company Logo'
},
{
id: 3,
title: 'Website Logo'
},
]
},
{
id: 2,
title: 'Brands',
sub_data: [
{
id: 25,
title: 'Company Brands'
},
{
id: 3,
title: 'Website Brands'
},
]
}
]
const mappedData = data.flatMap(obj => obj.sub_data)
console.log(mappedData)
Another solution would be to use the .forEach function of javascript.
const subData = [];
data.forEach(item => subData.push(...item.sub_data))

How to use setState in functional component React?

I was using classes. I changed it to functional components. But in handleLike method. I cant seem to understand how to use setState. Anyhelp with how to do it? In my current useState im getting array of objects. When I click on like button it displays an error that movies.map is not a function. Thankyou
movies.jsx
import React, { Component, useState } from "react";
import { getMovies } from "../services/fakeMovieService";
import Like from "./like";
function Movies() {
const initialMovies = getMovies();
const [movies, setMovies] = useState(initialMovies);
const handleDelete = (movie) => {
setMovies((movies) => movies.filter((m) => m._id !== movie._id));
};
const handleLike = (movie) => {
const movies = [...movies]
const index = movies.indexOf(movie)
movies[index] = { ...movie[index]}
movies[index].liked = !movies[index].liked
setMovies({ movies })
};
const { length: count } = movies;
if (count === 0) return <p>There are no movies in database</p>;
return (
<React.Fragment>
<p> Showing {count} movies in the database</p>
<table className="table">
<thead>
<tr>
<th>Title</th>
<th>Genre</th>
<th>Stock</th>
<th>Rate</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
{movies.map((movie) => (
<tr key={movie._id}>
<td>{movie.title}</td>
<td>{movie.genre.name}</td>
<td>{movie.numberInStock}</td>
<td>{movie.dailyRentalRate}</td>
<td>
<Like liked={movie.liked} onClick={()=> handleLike(movie)} />
</td>
<td>
<button
onClick={() => handleDelete(movie)}
className="btn btn-danger btn-sm"
>
Delete
</button>
</td>
</tr>
))}
</tbody>
</table>
</React.Fragment>
);
}
Like.jsx
class Like extends React.Component {
render() {
let classes = "fa fa-heart";
if (!this.props.liked) classes+= "-o"
return (
<i
className={classes}
aria-hidden="true"
onClick={this.props.onClick}
style={{cursor:"pointer"}}
></i>
);
}
}
JSON FILE
const movies = [
{
_id: "5b21ca3eeb7f6fbccd471815",
title: "Terminator",
genre: { _id: "5b21ca3eeb7f6fbccd471818", name: "Action" },
numberInStock: 6,
dailyRentalRate: 2.5,
publishDate: "2018-01-03T19:04:28.809Z",
liked: true,
},
{
_id: "5b21ca3eeb7f6fbccd471816",
title: "Die Hard",
genre: { _id: "5b21ca3eeb7f6fbccd471818", name: "Action" },
numberInStock: 5,
dailyRentalRate: 2.5
},
{
_id: "5b21ca3eeb7f6fbccd471817",
title: "Get Out",
genre: { _id: "5b21ca3eeb7f6fbccd471820", name: "Thriller" },
numberInStock: 8,
dailyRentalRate: 3.5
},
{
_id: "5b21ca3eeb7f6fbccd471819",
title: "Trip to Italy",
genre: { _id: "5b21ca3eeb7f6fbccd471814", name: "Comedy" },
numberInStock: 7,
dailyRentalRate: 3.5
},
{
_id: "5b21ca3eeb7f6fbccd47181a",
title: "Airplane",
genre: { _id: "5b21ca3eeb7f6fbccd471814", name: "Comedy" },
numberInStock: 7,
dailyRentalRate: 3.5
},
{
_id: "5b21ca3eeb7f6fbccd47181b",
title: "Wedding Crashers",
genre: { _id: "5b21ca3eeb7f6fbccd471814", name: "Comedy" },
numberInStock: 7,
dailyRentalRate: 3.5
},
{
_id: "5b21ca3eeb7f6fbccd47181e",
title: "Gone Girl",
genre: { _id: "5b21ca3eeb7f6fbccd471820", name: "Thriller" },
numberInStock: 7,
dailyRentalRate: 4.5
},
{
_id: "5b21ca3eeb7f6fbccd47181f",
title: "The Sixth Sense",
genre: { _id: "5b21ca3eeb7f6fbccd471820", name: "Thriller" },
numberInStock: 4,
dailyRentalRate: 3.5
},
{
_id: "5b21ca3eeb7f6fbccd471821",
title: "The Avengers",
genre: { _id: "5b21ca3eeb7f6fbccd471818", name: "Action" },
numberInStock: 7,
dailyRentalRate: 3.5
}
];
export function getMovies() {
return movies;
}
You have a few redundant object/array assignment in your code
So, update your handleLike like so:
const handleLike = (movie) => {
const _movies = [...movies];
const index = movies.indexOf(movie);
_movies[index].liked = !movies[index].liked;
setMovies(_movies);
};
Working Example:

TypeError: _SchoolProduct__WEBPACK_IMPORTED_MODULE_2___default.a.map is not a function

I am new at ReactJs and try to complete a task from the youtube channel.
I created array "products" in "SchoolProduct.js" then using props I passed the value in Product.js.
Now in App.js, I used map function to get data
(Maybe I understand something wrong about props or map function)
Here is SchoolProduct.js:
const products = [
{
id: "1",
name: "pencil",
price: 1,
description: "this is pencil"
},
{
id: "2",
name: "rubber",
price: 10,
description: "this is rubber"
}
]
this is my Product.js
import React from "react"
function Product(props)
{
return
(
<div>
<h2>{props.product.name}</h2>
<p>{props.product.price.toLocaleString("en-US", {style: "currency",
currency: "USD"})}
- {props.product.description}</p>
</div>
)
}
export default Product
and this my App.js
import React, { Component } from 'react';
import Product from "./Product"
import productsData from "./SchoolProduct"
function App(){
const productsComponents = productsData.map(item => <Product product=
{item}/>)
return (
<div>
{productsComponents}
</div>
)
}
export default App;
The Error is:
TypeError: _SchoolProduct__WEBPACK_IMPORTED_MODULE_2___default.a.map is not a function
its shows error in App.js line no 8, which is "const productsComponents"
I know I create a silly mistake, but I am trying to improve it
I have to export my error in default way,
const products = [
{
id: "1",
name: "pencil",
price: 1,
description: "this is pencil"
},
{
id: "2",
name: "rubber",
price: 10,
description: "this is rubber"
}
]
export default products
export default [
{
id: "1",
name: "Pencil",
price: 1,
description: "Perfect for those who can't remember things! 5/5 Highly recommend."
},
{
id: "2",
name: "Housing",
price: 0,
description: "Housing provided for out-of-state students or those who can't commute"
},
{
id: "3",
name: "Computer Rental",
price: 300,
description: "Don't have a computer? No problem!"
},
{
id: "4",
name: "Coffee",
price: 2,
description: "Wake up!"
},
{
id: "5",
name: "Snacks",
price: 0,
description: "Free snacks!"
},
{
id: "6",
name: "Rubber Duckies",
price: 3.50,
description: "To help you solve your hardest coding problems."
},
{
id: "7",
name: "Fidget Spinner",
price: 21.99,
description: "Because we like to pretend we're in high school."
},
{
id: "8",
name: "Sticker Set",
price: 14.99,
description: "To prove to other devs you know a lot."
}
]

Selected value is not displayed in async mode

I've got a react-select that I'm populating asyncly. The items display just fine however, after an item is selected the list reverts to Loading..., the spinner starts spinning and nothing appears in the select box.
I can only guess the selected value is not being persisted?? not sure. Complete=true in autocompleteLoad() has no affect. Setting isLoading=false has no affect. Here's the code...
import * as React from 'react';
import { RouteComponentProps } from 'react-router';
import * as models from '../models'
import Select from 'react-select'
import 'react-select/dist/react-select.css'
interface MovieActorState {
actor: models.Actor[]
loading: boolean
activeMovieId: number
activeActorId: number
acLoading: boolean,
acLabel?: string
}
const data = [{ value: 1, label: 'Mr Holland\'s Opus' },
{ value: 2, label: 'Braveheart' },
{ value: 3, label: 'Batman Forever' },
{ value: 1004, label: 'Star Wars' },
{ value: 1005, label: 'Goonies' },
{ value: 1006, label: 'ET' }];
const actors = [{ Id: 1, Name: 'Mel Gibson', Gender: 'Male', Age: 54, Picture: null },
{ Id: 2, Name: 'Val Kilmar', Gender: 'Male', Age: 49, Picture: null },
{ Id: 3, Name: 'Micheal Keaton', Gender: 'Male', Age: 60, Picture: null },
{ Id: 1002, Name: 'Diane Keaton', Gender: 'Female', Age: 49, Picture: null },
{ Id: 1003, Name: 'Tom Cruise', Gender: 'Male', Age: 55, Picture: null },
{ Id: 1006, Name: 'Richard Simmons', Gender: 'Male', Age: 59, Picture: null }];
const movieactors = [{ MovieId: 1, ActorId: 1 },
{ MovieId: 1, ActorId: 2 },
{ MovieId: 1, ActorId: 3 }];
export class Test extends React.Component<RouteComponentProps<{}>, MovieActorState> {
constructor(props) {
super(props);
this.that = this;
this.state = {
actor: [],
loading: true,
activeMovieId: 0,
activeActorId: 0,
acLoading: false
};
console.log('movieactor.fetch()', this.state)
this.setState({
actor: actors,
loading: false,
});
}
that;
public render() {
console.log('movieactor.render', this.state)
let contents = this.state.loading
? <p><em>Loading...</em></p>
: this.renderTable(this.state.actor, true);
return <div>
<h1>MovieActor</h1>
<label>Movie</label>
<Select.Async
name="form-field-name"
loadOptions={this.autocompleteLoad}
valueKey="value"
labelKey="label"
onChange={this.autocompleteSelect.bind(this)}
placeholder="Type to search"
value={this.state.activeMovieId + ''}
isLoading={false}
onClose={this.autocompleteClose.bind(this)}
/><br />
{contents}
</div>;
}
autocompleteSelect(e) {
console.log('movieactor.autocompleteSelect()', e, this.state)
this.setState({
actor: actors.filter((actor) => {
return (actor.Id > e.value);
}),
loading: false,
activeMovieId: e.value,
acLoading: false,
acLabel: e.label
});
}
autocompleteClose(e) {
console.log('movieactor.autocompleteClose()', e, this.state)
this.setState({ acLoading: false });
}
autocompleteLoad(input, callback) {
console.log('autocompleteLoad(' + input + ')')
if (input == null || input.length == 0) {
console.log('null')
callback(null, { complete: true })
return;
}
callback(null, {
options: data, complete: true
})
};
private renderTable(actor: models.Actor[], allowSort: boolean = false) {
let headings = this.renderTableHeadings(allowSort)
return <table className='table'>
<thead>
{headings}
</thead>
<tbody>
{actor.map(item =>
<tr key={item.Id}>
<td>
</td>
<td>{item.Id}</td>
<td>{item.Name}</td>
<td>{item.Gender}</td>
<td>{item.Age}</td>
<td>{item.Picture}</td>
</tr>
)}
</tbody>
</table>;
}
private renderTableHeadings(allowSort: boolean) {
return <tr>
<th></th>
<th>Id</th>
<th>Name</th>
<th>Gender</th>
<th>Age</th>
<th>Picture</th>
</tr>
}
}
Update: In my on-going effort to get this to work, it seems the hidden input with the value is missing. According to the react-select docs:
..but when I inspect the dom (after selecting item) it's not there...
I'm going to give this another day, before I replace the component with something else.
Code is working 100% fine,
Please check the WORKING DEMO , there might be some other code that would be affecting issue.

Resources