I am trying to conditionally render a component based on toggling of flag inside state. It looks like the state is getting updated but the component is not getting rendered. Can some one tell what is wring here. renderTree function updates the state, but render is not called then.
import React from "react";
import CheckboxTree from "react-checkbox-tree";
import "react-checkbox-tree/lib/react-checkbox-tree.css";
import { build } from "../data";
import { Input, Dropdown } from "semantic-ui-react";
import _ from "lodash";
class Widget extends React.Component {
constructor(props) {
super(props);
this.state = {
nodes: build(),
checked: [],
expanded: [],
isDropdownExpanded: false,
keyword: ""
};
}
onCheck = checked => {
this.setState({ checked }, () => {
console.log(this.state.checked);
});
};
onExpand = expanded => {
this.setState({ expanded }, () => {
console.log(this.state.expanded);
});
};
renderTree = () => {
this.setState(
prevState => {
return {
...prevState,
isDropdownExpanded: !prevState.isDropdownExpanded
};
},
() => {
console.log(this.state);
}
);
};
onSearchInputChange = (event, data, searchedNodes) => {
this.setState(prevState => {
if (prevState.keyword.trim() && !data.value.trim()) {
return {
expanded: [],
keyword: data.value
};
}
return {
expanded: this.getAllValuesFromNodes(searchedNodes, true),
keyword: data.value
};
});
};
shouldComponentUpdate(nextProps, nextState) {
if (this.state.keyword !== nextState.keyword) {
return true;
}
if (!_.isEqual(this.state.checked, nextState.checked)) {
return true;
}
if (_.isEqual(this.state.expanded, nextState.expanded)) {
return false;
}
return true;
}
getAllValuesFromNodes = (nodes, firstLevel) => {
if (firstLevel) {
const values = [];
for (let n of nodes) {
values.push(n.value);
if (n.children) {
values.push(...this.getAllValuesFromNodes(n.children, false));
}
}
return values;
} else {
const values = [];
for (let n of nodes) {
values.push(n.value);
if (n.children) {
values.push(...this.getAllValuesFromNodes(n.children, false));
}
}
return values;
}
};
keywordFilter = (nodes, keyword) => {
let newNodes = [];
for (let n of nodes) {
if (n.children) {
const nextNodes = this.keywordFilter(n.children, keyword);
if (nextNodes.length > 0) {
n.children = nextNodes;
} else if (n.label.toLowerCase().includes(keyword.toLowerCase())) {
n.children = nextNodes.length > 0 ? nextNodes : [];
}
if (
nextNodes.length > 0 ||
n.label.toLowerCase().includes(keyword.toLowerCase())
) {
n.label = this.getHighlightText(n.label, keyword);
newNodes.push(n);
}
} else {
if (n.label.toLowerCase().includes(keyword.toLowerCase())) {
n.label = this.getHighlightText(n.label, keyword);
newNodes.push(n);
}
}
}
return newNodes;
};
getHighlightText = (text, keyword) => {
const startIndex = text.indexOf(keyword);
return startIndex !== -1 ? (
<span>
{text.substring(0, startIndex)}
<span style={{ color: "red" }}>
{text.substring(startIndex, startIndex + keyword.length)}
</span>
{text.substring(startIndex + keyword.length)}
</span>
) : (
<span>{text}</span>
);
};
render() {
const { checked, expanded, nodes, isDropdownExpanded } = this.state;
let searchedNodes = this.state.keyword.trim()
? this.keywordFilter(_.cloneDeep(nodes), this.state.keyword)
: nodes;
return (
<div>
<Dropdown fluid selection options={[]} onClick={this.renderTree} />
{isDropdownExpanded && (
<div>
<Input
style={{ marginBottom: "20px" }}
fluid
icon="search"
placeholder="Search"
iconPosition="left"
onChange={(event, data) => {
this.onSearchInputChange(event, data, searchedNodes);
}}
/>
<CheckboxTree
nodes={searchedNodes}
checked={checked}
expanded={expanded}
onCheck={this.onCheck}
onExpand={this.onExpand}
showNodeIcon={true}
/>
</div>
)}
</div>
);
}
}
export default Widget;
Problem is in your shouldComponentUpdate method:
shouldComponentUpdate(nextProps, nextState) {
if (this.state.keyword !== nextState.keyword) {
return true;
}
if (!_.isEqual(this.state.checked, nextState.checked)) {
return true;
}
if (_.isEqual(this.state.expanded, nextState.expanded)) {
return false;
}
return true;
}
Since renderTree only changes isDropdownExpanded value, shouldComponentUpdate always returns false
If shouldComponenetUpdate returns true then your component re-renders, otherwise it dosen't.
In your code sandbox, it can be seen that every time you click on the dropdown, the shouldComponenetUpdate returns false for this condition
if (_.isEqual(this.state.expanded, nextState.expanded)) {
return false;
}
Either you need to change the state of this variable in your renderTree function or you need to re-write this condition as
if (_.isEqual(this.state.isDropdownExpanded, nextState.isDropdownExpanded)) {
return false;
}
Ciao, to force a re-render in React you have to use shouldComponentUpdate(nextProps, nextState) function. Something like:
shouldComponentUpdate(nextProps, nextState) {
return this.state.isDropdownExpanded !== nextState.isDropdownExpanded;
}
When you change isDropdownExpanded value, shouldComponentUpdate will be triggered and in case return is equal to true, component will be re-rendered. Here working example (based on your codesandbox).
Related
I've create an autocomplete search input bar using AWS Cloudscape. When I attempt to use the onSelect to save state, it does not save state. When I select a second item from the search bar, the first item then shows up. I assume this is a state async issue. I need the most recent selection, how do I get that?
import React from "react";
import Regions from "./trailforks_regions.json"
import RegionNames from "./region_names.json"
import * as dfd from "danfojs";
import { Autosuggest } from '#cloudscape-design/components';
export class AutoCompleteInputComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
df: new dfd.DataFrame(Regions),
value: null,
final_value: "",
options: [],
status: "loading",
regionContext: [],
uri_value: "",
loaded: false
};
}
lookupRegionUriName() {
console.log("Checking for uri region name on:" + this.state.final_value)
let region_name_split = this.state.final_value.split(",")[0]
let query_df = this.state.df.query(this.state.df["name"].eq(region_name_split))
let uri_name = query_df["region_uri_name"].values
console.log("found:" + uri_name)
return uri_name
}
handleChange = event => {
this.setState({ value: event.detail.value });
};
handleSelect = event => {
console.log(event) // This shows the correct data! But the set state does not set this data.
this.setState({ final_value: event.detail.value });
this.lookupRegionUriName(this.state.final_value)
};
handleLoadItems = ({ detail: { filteringText, firstPage, samePage } }) => {
this.filteringText = filteringText;
var my_options = RegionNames.filter(regions => regions.toLowerCase().startsWith(this.filteringText.toLowerCase()))
var region_values = []
for (var i = 0; i <= my_options.length; i++) {
region_values.push({ value: my_options[i] })
}
this.setState({
options: region_values.slice(0, 25),
status: 'loading',
});
};
enteredTextLabel = value => `Use: "${value}"`;
renderInput() {
if (this.state.df != null) {
const { status, value, options, final_value, uri_value } = this.state;
return (
<>
<Autosuggest
onChange={this.handleChange}
onSelect={this.handleSelect}
onLoadItems={this.handleLoadItems}
value={value}
options={options}
enteredTextLabel={this.enteredTextLabel}
ariaLabel="Autosuggest example with suggestions"
placeholder="Enter Trailforks Region Name"
empty="No matches found"
finishedText={this.filteringText ? `End of "${this.filteringText}" results` : 'End of all results'}
//status={status}
loadingText="searching"
filteringType="manual"
/>
</>
)
} else {
return (<></>)
}
}
render() {
return (
<>
{this.renderInput()}
</>
)
}
}
Figured it out. It was a state issue, here are the changes for future people:
import React from "react";
import Regions from "./regions.json"
import RegionNames from "./region_names.json"
import * as dfd from "danfojs";
import { Autosuggest } from '#cloudscape-design/components';
export class AutoCompleteInputComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
df: new dfd.DataFrame(Regions),
value: null,
final_value: "",
options: [],
status: "loading",
regionContext: [],
uri_value: "",
loaded: false
};
}
updateRegion(state_object) {
this.lookupRegionUriName()
return state_object.final_value
}
lookupRegionUriName() {
console.log("Checking for uri region name on:" + this.state.final_value)
let region_name_split = this.state.final_value.split(",")[0]
let query_df = this.state.df.query(this.state.df["name"].eq(region_name_split))
let uri_name = query_df["region_uri_name"].values
this.setState({uri_value: uri_name})
console.log("found:" + uri_name)
}
handleChange = event => {
this.setState({ value: event.detail.value });
};
handleSelect = event => {
console.log(event) // This shows the correct data! But the set state does not set this data.
this.setState({ final_value: event.detail.value });
this.lookupRegionUriName(this.state.final_value)
};
handleLoadItems = ({ detail: { filteringText, firstPage, samePage } }) => {
this.filteringText = filteringText;
var my_options = RegionNames.filter(regions => regions.toLowerCase().startsWith(this.filteringText.toLowerCase()))
var region_values = []
for (var i = 0; i <= my_options.length; i++) {
region_values.push({ value: my_options[i] })
}
this.setState({
options: region_values.slice(0, 25),
status: 'loading',
});
};
enteredTextLabel = value => `Use: "${value}"`;
renderInput() {
if (this.state.df != null) {
const { status, value, options, final_value, uri_value } = this.state;
return (
<>
<Autosuggest
onChange={this.handleChange}
onSelect={event => {this.setState({final_value: event.detail.value}, () => {this.updateRegion(this.state)})}}
//onSelect={this.handleSelect}
onLoadItems={this.handleLoadItems}
value={value}
options={options}
enteredTextLabel={this.enteredTextLabel}
ariaLabel="Autosuggest example with suggestions"
placeholder="Enter Trailforks Region Name"
empty="No matches found"
finishedText={this.filteringText ? `End of "${this.filteringText}" results` : 'End of all results'}
status={status}
loadingText="searching"
filteringType="manual"
/>
</>
)
} else {
return (<></>)
}
}
render() {
return (
<>
{this.renderInput()}
</>
)
}
}
Making a employee directory and I'm trying to convert class component into functional component. The nav, search, and table will render but the call to the api isn't working. Getting a
TypeError: users.filter is not a function Here is the class component that works and the in progress functional component. I can't figure out what is different.
import React, { Component } from "react";
import DataTable from "./DataTable";
import Nav from "./Nav";
import API from "../utils/API";
import "../styles/DataArea.css";
export default class DataArea extends Component {
state = {
users: [{}],
order: "descend",
filteredUsers: [{}]
}
headings = [
{ name: "Image", width: "10%" },
{ name: "Name", width: "10%" },
{ name: "Phone", width: "20%" },
{ name: "Email", width: "20%" },
{ name: "DOB", width: "10%" }
]
handleSort = heading => {
if (this.state.order === "descend") {
this.setState({
order: "ascend"
})
} else {
this.setState({
order: "descend"
})
}
const compareFnc = (a, b) => {
if (this.state.order === "ascend") {
// account for missing values
if (a[heading] === undefined) {
return 1;
} else if (b[heading] === undefined) {
return -1;
}
// numerically
else if (heading === "name") {
return a[heading].first.localeCompare(b[heading].first);
} else {
return a[heading] - b[heading];
}
} else {
// account for missing values
if (a[heading] === undefined) {
return 1;
} else if (b[heading] === undefined) {
return -1;
}
// numerically
else if (heading === "name") {
return b[heading].first.localeCompare(a[heading].first);
} else {
return b[heading] - a[heading];
}
}
}
const sortedUsers = this.state.filteredUsers.sort(compareFnc);
this.setState({ filteredUsers: sortedUsers });
}
handleSearchChange = event => {
console.log(event.target.value);
const filter = event.target.value;
const filteredList = this.state.users.filter(item => {
// merge data together, then see if user input is anywhere inside
let values = Object.values(item)
.join("")
.toLowerCase();
return values.indexOf(filter.toLowerCase()) !== -1;
});
this.setState({ filteredUsers: filteredList });
}
componentDidMount() {
API.getUsers().then(results => {
this.setState({
users: results.data.results,
filteredUsers: results.data.results
});
});
}
render() {
return (
<>
<Nav handleSearchChange={this.handleSearchChange} />
<div className="data-area">
<DataTable
headings={this.headings}
users={this.state.filteredUsers}
handleSort={this.handleSort}
/>
</div>
</>
);
}
}
import React, { useState, useEffect } from "react";
import DataTable from "./DataTable";
import Nav from "./Nav";
import API from "../utils/API";
import "../styles/DataArea.css";
const DataArea = () => {
const [users, setUsers] = useState([{}]);
const [order, setOrder] = useState("descend");
const [filteredUsers, setFilteredUsers] = useState([{}]);
const headings = [
{ name: "Image", width: "10%" },
{ name: "Name", width: "10%" },
{ name: "Phone", width: "20%" },
{ name: "Email", width: "20%" },
{ name: "DOB", width: "10%" },
];
const handleSort = (heading) => {
if (order === "descend") {
setOrder((order = "ascend"));
} else {
setOrder((order = "descend"));
}
const compareFnc = (a, b) => {
if (order === "ascend") {
// account for missing values
if (a[heading] === undefined) {
return 1;
} else if (b[heading] === undefined) {
return -1;
}
// numerically
else if (heading === "name") {
return a[heading].first.localeCompare(b[heading].first);
} else {
return a[heading] - b[heading];
}
} else {
// account for missing values
if (a[heading] === undefined) {
return 1;
} else if (b[heading] === undefined) {
return -1;
}
// numerically
else if (heading === "name") {
return b[heading].first.localeCompare(a[heading].first);
} else {
return b[heading] - a[heading];
}
}
};
const sortedUsers = filteredUsers.sort(compareFnc);
setFilteredUsers({ filteredUsers: sortedUsers });
};
let handleSearchChange = (event) => {
console.log(event.target.value);
const filtered = event.target.value;
const filteredList = users.filter((item) => {
// merge data together, then see if user input is anywhere inside
let values = Object.values(item).join("").toLowerCase();
return values.indexOf(filtered.toLowerCase()) !== -1;
});
setFilteredUsers((filteredUsers = filteredList));
};
useEffect(() => {
API.getUsers().then((results) => {
setUsers({
users: results.data.results,
filteredUsers: results.data.results,
});
});
}, []);
return (
<>
<Nav handleSearchChange={handleSearchChange} />
<div className="data-area">
<DataTable
headings={headings}
users={filteredUsers}
handleSort={handleSort}
/>
</div>
</>
);
};
export default DataArea;
The issue is in your useEffect hook where you shoehorn both users and filteredUsers into your users state.
useEffect(() => {
API.getUsers().then((results) => {
setUsers({
users: results.data.results,
filteredUsers: results.data.results,
});
});
}, []);
It should be
useEffect(() => {
API.getUsers().then((results) => {
setUsers(results.data.results);
setFilteredUsers(results.data.results),
});
}, []);
You want to populate both the users state, and separately the filteredUsers state.
An issue with setting the sorting order, you are mutating the order state:
if (order === "descend") {
setOrder((order = "ascend")); // <-- mutates
} else {
setOrder((order = "descend")); // <-- mutates
}
You can simply toggle between the two:
setOrder(order => order === 'ascend' ? 'descend' : 'ascend');
Another issue is at the end of your handleSort function:
const sortedUsers = filteredUsers.sort(compareFnc);
setFilteredUsers({ filteredUsers: sortedUsers });
This will nest the filteredUsers array, and also mutates the array in-place, it should probably use a functional state update to update from the previous state, use array.slice to make a copy, and then call sort.
setFilteredUsers(filteredUsers => filteredUsers.slice().sort(compareFnc));
In my component did update, I console.log this.state.checkedTeams and this.state.checked. These values are determined through the map in componentDidUpdate. They read as they should read.
However, when I console log the values in side the render, I get empty arrays.
When I try to set the state in componentDidUpdate, I exceed the maximum depth and throw an error.
The goal of this to be able to adjust this.state.checked based off of the state of a foreign component.
import React, { Component } from 'react'
import { Text, View } from 'react-native'
import { connect } from 'react-redux'
import { loadTeams, loadLeagues } from '../actions'
import Check from './CheckBox'
class TeamSelect extends Component {
constructor(props) {
super(props)
this.state = {
checked: [],
checkedTeams: [],
setOnce: 0
}
}
componentDidUpdate() {
this.state.checked.length = 0
this.props.team.map(
(v, i) => {
if(this.props.checkedLeagues.includes(v.league.acronym)){
this.state.checked.push(true)
this.state.checkedTeams.push(v.team_name)
} else{
this.state.checked.push(false)
}
}
)
console.log('checkedTeams', this.state.checkedTeams)
console.log('checked', this.state.checked)
}
// componentDidUpdate(){
// if(this.state.checked.length === 0) {
// this.props.team.map(
// (v, i) => {
// if(this.props.checkedLeagues.includes(v.league.acronym)){
// this.state.checked.push(true)
// this.state.checkedTeams.push(v.team_name)
// } else{
// this.state.checked.push(false)
// }
// }
// )
// }
// console.log('checked league', this.props.checkedLeagues)
// }
changeCheck = (index, name) => {
firstString = []
if(!this.state.checkedTeams.includes(name)) {
firstString.push(name)
} else {
firstString.filter(v => { return v !== name})
}
this.state.checkedTeams.map(
(v, i) => {
if(v !== name) {
firstString.push(v)
}
}
)
if(name === this.state.checkedTeams[0] && firstString.length === 0) {
this.setState({ checkMessage: `Don't you want something to look at?` })
} else {
if(!this.state.checkedTeams.includes(name)){
this.state.checkedTeams.push(name)
} else {
this.setState({checkedTeams: this.state.checkedTeams.filter(v => { return v !== name})})
}
this.state.checked[index] = !this.state.checked[index]
this.setState({ checked: this.state.checked })
// queryString = []
// firstString.map(
// (v, i) => {
// if (queryString.length < 1) {
// queryString.push(`?league=${v}`)
// } else if (queryString.length >= 1 ) {
// queryString.push(`&league=${v}`)
// }
// }
// )
// axios.get(`http://localhost:4000/reports${queryString.join('')}`)
// .then(response => {
// this.props.loadCards(response.data)
// })
}
// console.log('first string', firstString)
// console.log('in function', this.state.checkedTeams)
}
render() {console.log('in render - checkedTeams', this.state.checkedTeams)
console.log('in render - checked', this.state.checked)
return(
<View>
{
this.props.team === null ?'' : this.props.team.map(
(v, i) => {
return(
<View key={i}>
<Check
checked={this.state.checked[i]}
index={i}
value={v.team_name}
changeCheck={this.changeCheck}
/>
{ v.team_name === undefined ? null :
<Text>{v.team_name}</Text>}
</View>
)
}
)
}
</View>
)
}
}
function mapStateToProps(state) {
return {
team: state.team.team,
checkedLeagues: state.league.checkedLeagues
}
}
export default connect(mapStateToProps)(TeamSelect)
You are mutating state directly in several places (very bad). You have to use this.setState(new_state) or react looses track of what is changing. In order to change the state of one component based on the value of another, you need to have the dependent component as a child of the control component so it can receive the value as a prop or you need to pass a state changing function to the controlling element that is bound to the dependent element.
Read the official react docs on how state works and lifting state for more info.
https://reactjs.org/docs/faq-state.html
https://reactjs.org/docs/lifting-state-up.html
I am trying to set up validations in my todo list app in react; however this doesn't seem to work. Even if the form is empty the process still goes through and I don't know where the problem is coming from. I am just following a tutorial online since I'm pretty new to this and don't know how to do it myself.
import React from "react";
import * as TodoActions from "../actions/TodoActions";
import TodoStore from "../stores/TodoStore";
import Todo from './Todo.js'
import './Todos.css'
const formValid = ({ formErrors, ...rest }) => {
let valid = true;
Object.values(formErrors).forEach(val => {
val.length > 0 && (valid = false);
});
Object.values(rest).forEach(val => {
val === null && (valid = false);
});
return valid;
};
export default class Todos extends React.Component {
constructor() {
super();
this.state = {
todos: TodoStore.getAll(),
loading: true,
formErrors: {
todo: ""
}
};
TodoActions.receiveTodos()
}
componentWillMount() {
TodoStore.addChangeListener(this.getTodos);
}
componentWillUnmount() {
TodoStore.removeChangeListener(this.getTodos);
}
componentDidUpdate() {
TodoActions.receiveTodos();
}
getTodos = () => {
this.setState({
todos: TodoStore.getAll(),
loading: false
});
}
deleteTodo = (id) => {
TodoActions.deleteTodo(id);
}
addItem = (e) => {
e.preventDefault();
TodoActions.createTodo(this._inputElement.value)
}
handleChange = e => {
e.preventDefault();
const { name, value } = e.target;
let formErrors = { ...this.state.formErrors };
switch (name) {
case "todo":
formErrors.todo =
value.length < 0 ? "Task cannot be empty" : "";
break;
default:
break;
}
this.setState({ formErrors, [name]: value }, () => console.log(this.state));
}
render() {
const { todos } = this.state;
const { formErrors } = this.state;
let TodoComponents;
if (this.state.loading) {
TodoComponents = <h1>Loading...</h1>;
} else if(todos.length) {
TodoComponents = todos.map((todo) => {
return (
<div key={todo.id} className="todo-list">
<Todo key={todo.id} name={todo.name}/>
<div className="todo-btn"><a type="button" onClick={() => this.deleteTodo(todo.id)} className="delete-btn"><i class="fas fa-trash-alt"></i></a></div>
</div>
)
});
} else {
TodoComponents = <p>No tasks to show :)</p>
}
return (
<div className="main-container">
<div className="small-container">
<h1 className="title">All Tasks</h1>
<ul>{TodoComponents}</ul>
<form onSubmit={this.addItem}>
<input ref={(a) => this._inputElement = a} placeholder="Enter Task" className="input-form {formErrors.todo.length < 0 ? 'error' : null}"/>
{formErrors.todo.length < 0 && (
<span className="errorMessage">{formErrors.firstName}</span>
)}
<button type="submit" className="input-btn">Add</button>
</form>
</div>
</div>
);
}
}
I know the state is updating because 1. the 'Loading...' is going away, I can console log this.state.images to see the array. However when the state updates and the loading goes the searchbar shows up but the Card's within CardList do not.
They do show up when I search for a correct string, but not before.
If I pass this.state.images to CardList they show up perfectly. However when I move to the filteredImages they only show up when filtered.
Any ideas? Thanks in advance.
class App extends Component {
constructor() {
super();
this.state = {
images:[],
searchfield: ''
}
}
getLabels = (image) => {
const AuthKey = key.key;
const res = axios.post(`https://vision.googleapis.com/v1/images:annotate?key=${AuthKey}`, {
requests: [
{
image:{
source:{
imageUri: `http://storage.googleapis.com/${image}`
}
},
features:[
{
type:"LABEL_DETECTION",
maxResults:10
}
]
}
]
});
res.then(function (response) {
const results = response.data.responses[0].labelAnnotations;
const ex = results.map(result => {
return result.description;
});
return ex;
});
return res;
};
componentDidMount() {
imageFiles.imageFiles.forEach(img => {
this.getLabels(img).then(result => {
const results = result.data.responses[0].labelAnnotations;
const labels = results.map(result => {
return result.description;
});
//Add new values to the state
this.setState({images:[...this.state.images, {img, labels}]});
});
})
}
onSearchChange = (event) => {
this.setState({searchfield: event.target.value});
}
render() {
const filteredImages = this.state.images.filter(image => {
return image.labels.includes(this.state.searchfield.toLowerCase());
});
// Create an array of objects to store the image path and the labels detected from Google Vision
if (this.state.images.length === 0) {
return <h1>Loading...</h1>
} else {
return (
<Grid className="App">
<SearchBox searchChange={this.onSearchChange}/>
<CardList images={filteredImages} />
</Grid>
)}
}
}
export default App;
class App extends Component {
constructor() {
super();
this.state = {
images:[],
searchfield: '',
filteredImages:[]
}
}
getLabels = (image) => {
const AuthKey = key.key;
const res = axios.post(`https://vision.googleapis.com/v1/images:annotate?key=${AuthKey}`, {
requests: [
{
image:{
source:{
imageUri: `http://storage.googleapis.com/${image}`
}
},
features:[
{
type:"LABEL_DETECTION",
maxResults:10
}
]
}
]
});
res.then(function (response) {
const results = response.data.responses[0].labelAnnotations;
const ex = results.map(result => {
return result.description;
});
return ex;
});
return res;
};
componentDidMount() {
imageFiles.imageFiles.forEach(img => {
this.getLabels(img).then(result => {
const results = result.data.responses[0].labelAnnotations;
const labels = results.map(result => {
return result.description;
});
//Add new values to the state
this.setState({images:[...this.state.images, {img, labels}]});
this.setState({filteredImages:[...this.state.images, {img, labels}]});
});
})
}
onSearchChange = (event) => {
this.setState({searchfield: event.target.value});
let filteredImages = this.state.images.filter(image => {
return image.labels.includes(this.state.searchfield.toLowerCase());
});
this.setState({filteredImages});
}
render() {
// Create an array of objects to store the image path and the labels detected from Google Vision
if (this.state.images.length === 0) {
return <h1>Loading...</h1>
} else {
return (
<Grid className="App">
<SearchBox searchChange={this.onSearchChange}/>
<CardList images={this.state.filteredImages} />
</Grid>
)}
}
}
export default App;