Selected value is not displayed in async mode - reactjs

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.

Related

I am facing problem while filtering array using checkboxes using react hooks and typescript

I am trying to filter array as per gender (using checkbox ) but its not working. When i clicked on male checkbox it works but it wont work by clicking on female checkbox button. Here is my App.tsx. Need help to solve this?
import React, { useState } from "react";
const App = () => {
const [students, setStudents] = useState([
{ id: 1, title: "Akita from place1", race: "Akita", gender: 'female' },
{ id: 2, title: "Akita from place2", race: "Akita", gender: 'female' },
{ id: 3, title: "Akita from place3", race: "Akita", gender: 'female' },
{ id: 4, title: "Chihuahua from place4", race: "Chihuahua" , gender: 'male' },
{ id: 5, title: "Cockapoo from place5", race: "Cockapoo" , gender: 'male'},
{ id: 6, title: "Dachshund from place6", race: "Dachshund", gender: 'male' },
{ id: 7, title: "Dutch Shepherd from place7", race: "Dutch Shepherd" , gender: 'female' },
{ id: 8, title: "Bulldog from place8", race: "Bulldog", gender: 'male' },
{ id: 9, title: "Goldador from place9", race: "Goldador", gender: 'female' },
]);
const filterData = (e: any) => {
console.log(e.target.value);
if (e.target.value === "male") {
const filteredData = students.filter((student) => {
return student.gender === "male";
});
setStudents(filteredData);
}
if (e.target.value === "female") {
const filteredData = students.filter((student) => {
return student.gender === "female";
});
setStudents(filteredData);
}
};
return (
<div>
<h3>app</h3>
Male: <input type="checkbox" name='male' value='male' onChange={filterData} />
Female: <input type="checkbox" name='female' value='female' onChange={filterData} />
{students
.map((student: any) => {
return (
<div key={student.id}>
{student.id}-{student.title}-{student.race}-{student.gender}
</div>
);
})}
</div>
);
};
export default App;
const {
useState
} = React;
const App = () => {
const [students, setStudents] = React.useState([{
id: 1,
title: "Akita from place1",
race: "Akita",
gender: 'female'
},
{
id: 2,
title: "Akita from place2",
race: "Akita",
gender: 'female'
},
{
id: 3,
title: "Akita from place3",
race: "Akita",
gender: 'female'
},
{
id: 4,
title: "Chihuahua from place4",
race: "Chihuahua",
gender: 'male'
},
{
id: 5,
title: "Cockapoo from place5",
race: "Cockapoo",
gender: 'male'
},
{
id: 6,
title: "Dachshund from place6",
race: "Dachshund",
gender: 'male'
},
{
id: 7,
title: "Dutch Shepherd from place7",
race: "Dutch Shepherd",
gender: 'female'
},
{
id: 8,
title: "Bulldog from place8",
race: "Bulldog",
gender: 'male'
},
{
id: 9,
title: "Goldador from place9",
race: "Goldador",
gender: 'female'
},
]);
const [filtered, setFiltered] = useState([])
const filterData = (e) => {
const {value, checked} = e.target;
//check if value not in state and checked is true then add value to state
if(!filtered.includes(value) && checked){
setFiltered([...filtered, value])
}else{
setFiltered(filtered.filter(f=>f!==value))
}
};
const filteredStudent = filtered.length > 0 ? students.filter(s => filtered.includes(s.gender)) : students;
return (
<div>
<h3>app</h3>
Male: <input type="checkbox" name='male' value='male' onChange={filterData}/>
Female: <input type="checkbox" name='female' value='female' onChange={filterData}/>
{filteredStudent
.map((student) => {
return (
<div key={student.id}>
{student.id}-{student.title}-{student.race}-{student.gender}
</div>
);
})}
</div>
);
};
// Render it
ReactDOM.createRoot(
document.getElementById("root")
).render( <
App / >
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
It does not work because you are filtering all the female out and updating the state, this means that after clicking one button once, the filtered values does not exist in state anymore. To fix this, you could track the filter type in state and derive the filtered students during render.
Note that you could use radio buttons so only one gender can be selected at a time.
import React, {useState} from "react";
const App = () => {
const [students, setStudents] = useState([
{id: 1, title: "Akita from place1", race: "Akita", gender: 'female'},
{id: 2, title: "Akita from place2", race: "Akita", gender: 'female'},
{id: 3, title: "Akita from place3", race: "Akita", gender: 'female'},
{id: 4, title: "Chihuahua from place4", race: "Chihuahua", gender: 'male'},
{id: 5, title: "Cockapoo from place5", race: "Cockapoo", gender: 'male'},
{id: 6, title: "Dachshund from place6", race: "Dachshund", gender: 'male'},
{id: 7, title: "Dutch Shepherd from place7", race: "Dutch Shepherd", gender: 'female'},
{id: 8, title: "Bulldog from place8", race: "Bulldog", gender: 'male'},
{id: 9, title: "Goldador from place9", race: "Goldador", gender: 'female'},
]);
const [filter, setFilter] = useState<null | string>(null)
const filterData = (e) => {
setFilter(e.target.checked ? e.target.value : null)
};
const filteredStudent = filter ? students.filter(s => s.gender === filter) : students;
return (
<div>
<h3>app</h3>
Male: <input type="checkbox" name='male' value='male' onChange={filterData}/>
Female: <input type="checkbox" name='female' value='female' onChange={filterData}/>
{filteredStudent
.map((student) => {
return (
<div key={student.id}>
{student.id}-{student.title}-{student.race}-{student.gender}
</div>
);
})}
</div>
);
};
export default App;

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:

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

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 to sort a multi-dimension array using _.sortBy function in react JS?

Below is the code :
import _ from 'lodash'
import React, { Component } from 'react'
import { Table } from 'semantic-ui-react'
const tableData = [
{
name: 'John',
age: 15,
gender: 'Male',
"providerExtension":
{
name: "true",
age: 45,
key: "isEncrypt"
}
},
{
name: 'Amber',
age: 42,
gender: 'Female',
"providerExtension":
{
name: "true",
age: 15,
key: "isEncrypt"
}
},
{
name: 'Leslie',
age: 25,
gender: 'Female',
"providerExtension":
{
name: "true",
age: 25,
key: "isEncrypt"
}
},
{
name: 'Ben',
age: 70,
gender: 'Male',
"providerExtension":
{
age: 75,
name: "true",
key: "isEncrypt"
}
},
]
export default class TableExampleSortable extends Component {
state = {
column: null,
data: tableData,
direction: null,
}
handleSort = clickedColumn => () => {
const { column, data, direction } = this.state
if (column !== clickedColumn) {
this.setState({
column: clickedColumn,
data: _.sortBy(data, [clickedColumn]),
direction: 'ascending',
})
return
}
this.setState({
data: data.reverse(),
direction: direction === 'ascending' ? 'descending' : 'ascending',
})
}
render() {
const { column, data, direction } = this.state
return (
<Table sortable celled fixed>
<Table.Header>
<Table.Row>
<Table.HeaderCell
sorted={column === 'name' ? direction : null}
onClick={this.handleSort('name')}
>
Name
</Table.HeaderCell>
<Table.HeaderCell
sorted={column === 'age' ? direction : null}
onClick={this.handleSort('age')}
>
Age
</Table.HeaderCell>
<Table.HeaderCell
sorted={column === 'gender' ? direction : null}
onClick={this.handleSort('gender')}
>
Gender
</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{_.map(data, (data) => (
<Table.Row key={name}>
<Table.Cell>{data.name}</Table.Cell>
<Table.Cell>{data.providerExtension.age}</Table.Cell>
<Table.Cell>{data.gender}</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
)
}
}
Actually i am very new to react js. I have a requirement to sort the multi dimensional array. In above mentioned code i want to sort the key present in the nested array providerExtension.name.
I dont want to sort the tableData.name.
I am using _.sortBy function to sort the array..
Can anyone plz help me?
You can use this solution: lodash orderBy on nested property. For example:
this.setState({
column: clickedColumn,
data: _.sortBy(data, `providerExtension.${clickedColumn}`),
direction: 'ascending',
})
Ref:
Template Literals

Reactjs With Handsontable cell validation function is not invoking

I have declared cell validation function but in cells property but it is not getting invoked and no error is also thrown
I have declared the function definition as member function and trying to call it from componentDidMount function
Also some suggestions regarding using handsontable with react would be great!
Below is my code
import React from 'react';
import Handsontable from 'handsontable/dist/handsontable';
let hot = {};
let columns = [];
let data = '';
class SpreadSheet extends React.Component {
constructor(props){
super(props);
this.state = {
data : [
{year: "2016", name: 'John', age: 23, contact: 8000142880},
{year: "2015", name: 'Doe', age: 22, contact: 9494858568},
{year: "2013", name: 'Jack', age: 21, contact: 7878989825},
{year: "2012", name: 'Joe', age: 20, contact: 9898454526},
]
}
columns = [
{ data: 'year', type: 'text' },
{ data: 'name', type: 'text' },
{ data: 'age', type: 'numeric' },
{ data: 'contact', type: 'numeric' }
]
}
getData = () => {
var datafromtable = document.getElementById('foo');
//console.log(datafromtable);
data = hot.getData();
console.log(data);
}
negativeValueRenderer = () => (instance, td, row, col, prop, value, cellProperties) => {
console.log('arguments');
//Handsontable.renderers.TextRenderer.apply(this, arguments);
//return;
}
componentDidMount() {
var container = document.getElementById('foo');
hot = new Handsontable(container, {
data: this.state.data,
minSpareCols: 1 ,
minSpareRows: 1,
minCols: 5,
minRows: 5,
rowHeaders: true,
colHeaders: ['Year','Name','Age','Contact'],
columns: columns,
cells: function (row, col, prop) {
console.log(this);
this.renderer = this.negativeValueRenderer;//not getting triggered
},
contextMenu: true
});
}
render() {
return (
<div>
<div id="foo"></div>
<button onClick = {this.getData}>Get Data</button>
{data}
</div>
);
}
}
export default SpreadSheet;
Are you trying to apply the negativeValueRenderer to each cell, or just the Age column?
Give this a shot:
constructor(props) {
super(props);
this.negativeValueRenderer = this.negativeValueRenderer.bind(this);
}
...
negativeValueRenderer(instance, td, row, col, prop, value, cellProperties) {
renderers.NumericRenderer.apply(this, arguments);
if (parseInt(value, 10) < 0) {
td.style.color = 'red';
}
}
...
hot = new Handsontable(container, {
data: this.state.data,
minSpareCols: 1 ,
minSpareRows: 1,
minCols: 5,
minRows: 5,
rowHeaders: true,
colHeaders: ['Year','Name','Age','Contact'],
columns: columns,
columns={[
{
data: 'year',
},
{
data: 'name',
},
{
data: 'age',
validator: this.negativeValueValidator,
},
{
data: 'contact',
},
contextMenu: true
});

Resources