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:
Related
I'm building a dashboard of sorts as part of a project to learn React and NextJS. I'm stuck on an issue that I have found no solution to online. Any help is appreciated!
So I have a page called 'departments.js', which shows a table like the one in the image below.
I get the data to fill in the rows and columns from 2 hardcoded JSON stringa:
const data = [
{ name: "Customer Service", num: '56', dept:'custserv' },
{ name: "Human Resources", num: '21', dept:'hr' },
{ name: "Quality Assurance", num: '13', dept:'qa' },
{ name: "Marketing", num: '30', dept:'mark' },
{ name: "Research and Development", num: '17', dept:'rnd' },
{ name: "Operations", num: '49', dept:'ops' },
{ name: "Sales", num: '37', dept:'sales' },
{ name: "Distribution", num: '26', dept:'dist' },
{ name: "IT", num: '12', dept:'it' },
]
const column = [
{ heading: 'Department', value: 'name' },
{ heading: 'No. of Employees', value: 'num' },
{ heading: 'Actions', value: 'dept' },
];
The dept field in the JSON string should store the name of the js file for that department.
What I want to achieve is - when the user clicks the View Department button in the marketing row, the app navigates to /mark.js.
I have a separate component for the table:
Table.js
const Table = ({ data, column }) => {
return (
<table>
<thead>
<tr>
{column.map((item, index) => <TableHeadItem item={item} />)}
</tr>
</thead>
<tbody>
{data.map((item, index) => <TableRow item={item} column={column} />)}
</tbody>
</table>
)
}
const TableHeadItem = ({ item }) => <th>{item.heading}</th>
const TableRow = ({ item, column }) => (
<tr>
{column.map((columnItem, index) => {
if(columnItem.value.includes('.')) {
const itemSplit = columnItem.value.split('.')
return <td>{item[itemSplit[0]][itemSplit[1]]}</td>
}
return <td>{item[`${columnItem.value}`]}</td>
})}
</tr>
)
export default Table
Here is how I build the table in the departments.js file:
return(
<div className={styles.tableCont}>
<Table data={data} column={column} /> {/* data and column JSONs*/}
</div>
)
I tried declaring a function to build the button for each row with the dept from the data JSON, but I'm not able to do it. Please advise on how to go about this.
My failed attempt
const viewDeptBtn = (dept) => {
return (
<button className={styles.viewDeptBtn} onClick={() => router.push('/'+dept)}>
View Department
</button>
)
}
const column = [
{ heading: 'Department', value: 'name' },
{ heading: 'No. of Employees', value: 'num' },
{ heading: 'Actions', value: viewDeptBtn('dept') },
];
If your data is shaped like this:
const data = [
{ name: "Customer Service", num: '56', dept:'custserv' },
{ name: "Human Resources", num: '21', dept:'hr' },
{ name: "Quality Assurance", num: '13', dept:'qa' },
{ name: "Marketing", num: '30', dept:'mark' },
{ name: "Research and Development", num: '17', dept:'rnd' },
{ name: "Operations", num: '49', dept:'ops' },
{ name: "Sales", num: '37', dept:'sales' },
{ name: "Distribution", num: '26', dept:'dist' },
{ name: "IT", num: '12', dept:'it' },
]
Then to produce the table rows you'd just have to do the following:
const Button = ({ dept }) => (
<button onClick={() => router.push(`/${dept}`)}>View Department</button>
);
const TableRow = ({ item }) => (
<tr>
<td>{item.name}</td>
<td>{item.num}</td>
<td>
<Button dept={item.dept} />
</td>
</tr>
);
const TableHeadItem = ({ item }) => <th>{item.heading}</th>;
export default function Table({data, column}) {
return (
<table>
<thead>
<tr>
{column.map((item, index) => (
<TableHeadItem item={item} key={index} />
))}
</tr>
</thead>
<tbody>
{data.map((item) => (
<TableRow item={item} key={item.num} />
))}
</tbody>
</table>
);
}
This is shop page.jsx file here i want to show the list of filtered items at the top of the page. Here im using data list for getting all the details. Here i want to show length of filtered products at showing 9 0f 9 products.
import React, { useEffect, useState } from 'react';
import EmptyView from '../components/common/EmptyView';
import FilterPanel from '../components/Home/FilterPanel';
import List from './Home/List';
// import SearchBar from '../../components/Home/SearchBar';
import { dataList } from '../constants';
// import './styles.css';
import '../App.css';
import ButtonAppBar from "./Header";
const Shop = () => {
// const [selectedCategory, setSelectedCategory] = useState(null);
// const [selectedRating, setSelectedRating] = useState(null);
const [selectedPrice, setSelectedPrice] = useState([0, 150]);
const [cuisines, setCuisines] = useState([
{ id: 1, checked: false, label: 'Furniture' },
{ id: 2, checked: false, label: 'Decoration' },
{ id: 3, checked: false, label: 'Bedding' },
{ id: 4, checked: false, label: 'Lighting' },
{ id: 5, checked: false, label: 'Bath&Shower' },
{ id: 6, checked: false, label: 'Curtains' },
{ id: 7, checked: false, label: 'Toys' },
]);
const [brand, setBrand] = useState([
{ id: 1, checked: false, label: 'Poliform' },
{ id: 2, checked: false, label: 'Rochie Bobois' },
{ id: 3, checked: false, label: 'Edra' },
{ id: 4, checked: false, label: 'Kartell' },
]);
const [availability, setAvailability] = useState([
{ id: 1, checked: false, label: 'Onstock' },
{ id: 2, checked: false, label: 'Outofstock' },
]);
const [list, setList] = useState(dataList);
const [resultsFound, setResultsFound] = useState(true);
// const [searchInput, setSearchInput] = useState('');
// const handleSelectCategory = (event, value) =>
// !value ? null : setSelectedCategory(value);
// const handleSelectRating = (event, value) =>
// !value ? null : setSelectedRating(value);
const handleChangeChecked = (id) => {
const cusinesStateList = cuisines;
const changeCheckedCuisines = cusinesStateList.map((item) =>
item.id === id ? { ...item, checked: !item.checked } : item
);
setCuisines(changeCheckedCuisines);
};
const handleChangeCheckeds = (id) => {
const brandStateList = brand;
const changeCheckedsBrand = brandStateList.map((item) =>
item.id === id ? { ...item, checked: !item.checked } : item
);
setBrand(changeCheckedsBrand);
};
const handleChangeCheckedss = (id) => {
const availabilityStateList = availability;
const changeCheckedssAvailability = availabilityStateList.map((item) =>
item.id === id ? { ...item, checked: !item.checked } : item
);
setAvailability(changeCheckedssAvailability);
};
const handleChangePrice = (event, value) => {
setSelectedPrice(value);
};
const applyFilters = () => {
let updatedList = dataList;
// // Rating Filter
// if (selectedRating) {
// updatedList = updatedList.filter(
// (item) => parseInt(item.rating) === parseInt(selectedRating)
// );
// }
// // Category Filter
// if (selectedCategory) {
// updatedList = updatedList.filter(
// (item) => item.category === selectedCategory
// );
// }
// Cuisine Filter
const cuisinesChecked = cuisines
.filter((item) => item.checked)
.map((item) => item.label.toLowerCase());
if (cuisinesChecked.length) {
updatedList = updatedList.filter((item) =>
cuisinesChecked.includes(item.cuisine)
);
}
// brand filter
const brandChecked = brand
.filter((item) => item.checked)
.map((item) => item.label.toLowerCase());
if (brandChecked.length) {
updatedList = updatedList.filter((item) =>
brandChecked.includes(item.brand)
);
}
// availabilty filter
const availabilityChecked = availability
.filter((item) => item.checked)
.map((item) => item.label.toLowerCase());
if (availabilityChecked.length) {
updatedList = updatedList.filter((item) =>
availabilityChecked.includes(item.availability)
);
}
// // Search Filter
// if (searchInput) {
// updatedList = updatedList.filter(
// (item) =>
// item.title.toLowerCase().search(searchInput.toLowerCase().trim()) !==
// -1
// );
// }
// // Price Filter
const minPrice = selectedPrice[0];
const maxPrice = selectedPrice[1];
updatedList = updatedList.filter(
(item) => item.price >= minPrice && item.price <= maxPrice
);
setList(updatedList);
!updatedList.length ? setResultsFound(false) : setResultsFound(true);
};
useEffect(() => {
applyFilters();
}, [cuisines, brand, availability, selectedPrice]);
return (
<div>
<ButtonAppBar />
<div className='home'>
{/* Search Bar */}
{/* <SearchBar
value={searchInput}
changeInput={(e) => setSearchInput(e.target.value)}
/> */}
<br /><br /><br /> <div className='home_panelList-wrap'>
{/* Filter Panel */}
<div className='home_panel-wrap'>
<FilterPanel
// selectedCategory={selectedCategory}
// selectCategory={handleSelectCategory}
// selectedRating={selectedRating}
selectedPrice={selectedPrice}
// selectRating={handleSelectRating}
cuisines={cuisines}
changeChecked={handleChangeChecked}
brand={brand}
changeCheckeds={handleChangeCheckeds}
availability={availability}
changeCheckedss={handleChangeCheckedss}
changePrice={handleChangePrice}
/>
</div>
{/* List & Empty View */}
<div className='home_list-wrap'>
<h6>Showing
<span style={{ color: "#bd744c" }}><b>{dataList.length}</b></span> of
<span style={{ color: "#bd744c" }}><b>9</b></span>
Products</h6>
{resultsFound ? <List list={list} /> : <EmptyView />}
</div>
</div>
</div>
</div>
);
};
export default Shop;
This is constant.js file from here we are getting all our details in shop.jsx file.
export const dataList = [
{
id: 1,
title: 'AwesomeLamp',
cuisine: 'lighting',
price: 40,
image: '/images/AwesomeLamp.png',
brand: 'poliform',
availability: 'onstock',
name: 'AwesomeLamp',
tagName: 'AwesomeLamp'
},
{
id: 2,
title: 'CozySofa',
cuisine: 'furniture',
price: 150,
image: '/images/CozySofa.png',
brand: 'edra',
availability: 'outofstock',
name: 'CozySofa',
tagName: 'CozySofa'
},
{
id: 3,
title: 'AwesomeCandle',
cuisine: 'lighting',
price: 15,
image: '/images/AwesomeCandle.png',
brand: 'kartell',
availability: 'onstock',
name: 'AwesomeCandle',
tagName: 'AwesomeCandle',
},
{
id: 4,
title: 'FancyChair',
cuisine: 'furniture',
price: 70,
image: '/images/FancyChair.png',
brand: 'poliform',
availability: 'outofstock',
name: 'FancyChair',
tagName: 'FancyChair'
},
{
id: 5,
title: 'ChineseTeapot',
cuisine: 'decoration',
price: 50,
image: '/images/ChineseTeapot.png',
brand: 'rochie bobois',
availability: 'onstock',
name: 'ChineseTeapot',
tagName: 'ChineseTeapot'
},
{
id: 6,
title: 'SoftPillow',
cuisine: 'bedding',
price: 30,
image: '/images/SoftPillow.png',
brand: 'edra',
availability: 'onstock',
name: 'SoftPillow',
tagName: 'SoftPillow'
},
{
id: 7,
title: 'WoodenCasket',
cuisine: 'decoration',
price: 20,
image: '/images/WoodenCasket.png',
brand: 'kartell',
availability: 'onstock',
name: 'WoodenCasket',
tagName: 'WoodenCasket'
},
{
id: 8,
title: 'AwesomeArmChair',
cuisine: 'furniture',
price: 90,
image: '/images/AwesomeArmChair.png',
brand: 'poliform',
availability: 'onstock',
name: 'AwesomeArmchair',
tagName: 'AwesomeArmchair'
},
{
id: 9,
title: 'CoolFlower',
cuisine: 'decoration',
price: 20,
image: '/images/CoolFlower.png',
brand: 'none',
availability: 'onstock',
name: 'CoolFlower',
tagName: 'CoolFlower'
},
];
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 => (...
How can I make a separate trigger for each Collapsible element with a single useState (preferably)?
CodeSandBox - https://codesandbox.io/s/separate-triggers-question-forked-vgs5b
App.js
import React from "react";
import Collapsible from "react-collapsible";
function App() {
const database = [
{ id: 1, name: "Name1", description: "Desc1" },
{ id: 2, name: "Name2", description: "Desc2" },
{ id: 3, name: "Name3", description: "Desc3" },
{ id: 4, name: "Name4", description: "Desc4" },
{ id: 5, name: "Name5", description: "Desc5" }
];
const [items, setItems] = React.useState(database);
const [open, setOpen] = React.useState(false);
return (
<div className="tracker_master">
{items.map((item, index) => (
<div onClick={() => setOpen(!open)} key={item.id}>
{item.name.toUpperCase()}
<Collapsible open={open}>
<div>{item.description.toUpperCase()}</div>
</Collapsible>
</div>
))}
</div>
);
}
export default App;
To have seperate triggers for each item I recommend to abstract each element into its own component that has its own open state.
So you could do something like this:
const Item = ({ item }) => {
const [open, setOpen] = React.useState(false);
return (
<div onClick={() => setOpen(!open)} key={item.id}>
{item.name.toUpperCase()}
<Collapsible open={open}>
<div>{item.description.toUpperCase()}</div>
</Collapsible>
</div>
);
};
function App() {
const database = [
{ id: 1, name: "Name1", description: "Desc1" },
{ id: 2, name: "Name2", description: "Desc2" },
{ id: 3, name: "Name3", description: "Desc3" },
{ id: 4, name: "Name4", description: "Desc4" },
{ id: 5, name: "Name5", description: "Desc5" }
];
return (
<div className="tracker_master">
{database.map((item) => (
<Item item={item} key={item.id} />
))}
</div>
);
}
sandbox example
If you want to use single useState then your single state object should manage open flag of all the items as shown below (I have updated your code and tested its working fine).
openFlags is single state which maintains the open flag of each item by id and use it for triggering collabse and expand the items independently.
import React from "react";
import Collapsible from "react-collapsible";
function App() {
const database = [
{ id: 1, name: "Name1", description: "Desc1" },
{ id: 2, name: "Name2", description: "Desc2" },
{ id: 3, name: "Name3", description: "Desc3" },
{ id: 4, name: "Name4", description: "Desc4" },
{ id: 5, name: "Name5", description: "Desc5" }
];
const [items, setItems] = React.useState(database);
let initialOpenFlags = {}
items.forEach((i) => {
initialOpenFlags = {
...initialOpenFlags,
[i.id]: false
};
});
const [openFlags, setOpenFlags] = React.useState(initialOpenFlags);
return (
<div className="tracker_master">
{items.map((item, index) => (
<div
onClick={() =>
setOpenFlags({ ...openFlags, [item.id]: !openFlags[item.id] })
}
key={item.id}
>
{item.name.toUpperCase()}
<Collapsible open={openFlags[item.id]}>
<div>{item.description.toUpperCase()}</div>
</Collapsible>
</div>
))}
</div>
);
}
export default App;
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.