Conditional rendering another antd component reactsjs - javascript

I want to make my antd reactjs components create/ show the next component (in the order in index.js) when the component selects an option/ fills in the input field.
index.js
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<div id = "overall" >
<div id ="but1" ><ListButton /></div>
<div id = "but2" ><ListButton /></div>
<div id = "but3" ><ListButton /></div>
<div id = "num1" ><NumInput /></div>
<div id = "andor1" ><AndOrButton /></div>
<div id = "but4" ><ListButton /></div>
<div id = "but5" ><ListButton /></div>
<div id = "but6" ><ListButton /></div>
<div id = "num2" ><NumInput /></div>
<div id = "andor2" ><AndOrButton /></div>
<div id = "but7" ><ListButton /></div>
<div id = "but8" ><ListButton /></div>
<div id = "but9" ><ListButton /></div>
<div id = "num3" ><NumInput /></div>
</div>
</React.StrictMode>
);
Following are the components
listbutton.js
import { Select } from 'antd';
const handleChange = (value) => {
console.log(`selected ${value}`);
};
const ListButton = () => (
<Select
style={{
width: 200,
}}
onChange={handleChange}
placeholder = "Select option"
options={[
{
options: [
{
label: 'Item 1',
value: 'Item 1',
},
{
label: 'Item 2',
value: 'Item 2',
},
{
label: 'Item 3',
value: 'Item 3',
},
],
},
]}
/>
);
AndOrButton.js
import { Select } from 'antd';
const handleChange = (value) => {
console.log(`selected ${value}`);
};
const AndOrButton = () => (
<Select
style={{
width: 200,
}}
onChange={handleChange}
placeholder = "Select option"
options={[
{
options: [
{
label: 'AND',
value: 'AND',
},
{
label: 'OR',
value: 'OR',
},
],
},
]}
/>
);
NumInput.js
import { Input } from 'antd';
const NumInput = () => <Input placeholder="Input Value" />;
export default NumInput;
I tried different ways like using a dictionary and looping but I couldn't get to the solution.

Related

How to get selected values from multiple dropdowns on button click using ReactJs

I want to console.log() multiple values from five different dropdowns on a button click. I have done this for one dropdown, but I don't know how to do it for more. I'm still a beginner.
Here's my code:
export default function Suma() {
const typedemande = [
{ value: "first", label: "first" },
{ value: "second", label: "second" },
];
const [message, setMessage] = useState('');
const handleChange = event => {
setMessage(event);
};
const handleClick = event => {
event.preventDefault();
console.log(message);
};
return (
<div>
<div className="col-lg">
<Select placeholder="choose" id="message" className="react-dropdown " name="message" onChange={handleChange}
value={message}
isClearable
isSearchable={false}
classNamePrefix="dropdown"
options={typedemande}
/>
</div>
<div className="text-center">
<button className="mr-2 btn btn-primary" onClick={handleClick}>Click me</button>
</div>
</div>
);
};
I hope you are looking for this one:
export default function App() {
const typedemande = [
{ value: "first", label: "first" },
{ value: "second", label: "second" },
{ value: "third", label: "third" },
{ value: "fourth", label: "fourth" },
{ value: "five", label: "five" },
];
const [showAll, setShowAll ] = useState([]);
const [dropdowns,setDrodowns] = useState({
'message1': '',
'message2': '',
'message3': '',
'message4': '',
'message5': '',
});
const handleChange = (event) => {
setDrodowns({...dropdowns,[event.target.name]:event.target.value});
}
const handleClick = (event) => {
event.preventDefault(); // if you use the element inside `form` then it would prevent to submit
console.log(dropdowns);//to log the values in console
setShowAll(Object.values(dropdowns));// to show the changes in UI
}
return (
<div>
<div className="col-lg">
<Select
name="message1"
onChange={handleChange}
value={"second"}
options={typedemande}
/>
<Select
name="message2"
onChange={handleChange}
value={"second"}
options={typedemande}
/>
<Select
name="message3"
onChange={handleChange}
value={"second"}
options={typedemande}
/>
<Select
name="message4"
onChange={handleChange}
value={"second"}
options={typedemande}
/>
<Select
name="message5"
onChange={handleChange}
value={"second"}
options={typedemande}
/>
</div>
<hr/>
<ul>
{ showAll.map((val,i)=><li key={i}>{i+1} --- {val}</li>) }
</ul>
<hr/>
<div className="text-center">
<button className="mr-2 btn btn-primary" onClick={handleClick}>Click me</button>
</div>
</div>
);
}
For details check the code sandbox link
Out put
Edit: Based on user comments I edited the answer
You could pass a parameter to your handleChange.
const handleChange = (event, position) => {
console.log(position);
};
<Select onChange={(e) => handleChange(e, 1)} />
<Select onChange={(e) => handleChange(e, 2)} />
<Select onChange={(e) => handleChange(e, 3)} />
Improving axtck's answer, you can get each select value like below
import React, {useState} from 'react';
import Select from 'react-select';
export function App(props) {
const typedemande = [
{ value: "first", label: "first" },
{ value: "second", label: "second" },
];
const [messages, setMessages] = useState([]);
const handleChange = (event, pos) => {
console.log(pos)
console.log(event.value)
let mz = [...messages];
if (mz.length > 0 && mz.findIndex(msg => msg.index == pos) > -1) {
mz[mz.findIndex(msg => msg.index == pos)] = event.value;
setMessages(mz);
}
else {
mz.push({
index: pos,
value: event.value
});
setMessages(mz);
}
};
const handleClick = event => {
event.preventDefault();
for (let i = 0; i < messages.length; i++)
console.log(messages[i].value)
};
return (
<div>
<div className="col-lg">
<Select placeholder="choose" id="message" className="react-dropdown " name="message" onChange={(e) => handleChange(e, 1)}
value={messages[0] ? messages[0].label : ''}
isClearable
isSearchable={false}
classNamePrefix="dropdown"
options={typedemande}
/>
<Select placeholder="choose" id="message" className="react-dropdown " name="message" onChange={(e) => handleChange(e, 2)}
value={messages[1] ? messages[1].label : ''}
isClearable
isSearchable={false}
classNamePrefix="dropdown"
options={typedemande}
/>
</div>
<div className="text-center">
<button className="mr-2 btn btn-primary" onClick={handleClick}>Click me</button>
</div>
</div>
);
}

Why am I unable to target a mapped out <li> from the button mapped out with it with React?

I am in the process of learning react and am working on a pretty simple food ordering app that maps through a list of meals in a component named AvailableMeals and would allow a user to hit the add to cart button on each mapped out item to add it to cart.
Right now though, I am unable to get my add button to target the LI that it is connected to. I have tried putting the targeting function in multiple places and tried to target different things (this, this.name, e.target.value, etc) but everything still comes up with undefined or throws an unable to read undefined error. I have even tried to use useContext, but still nothing. I will add the components that are relevant to this below.
My component tree is MealForm -> MealItem -> MealItemForm -> Input and will paste the markup here in that order.
Thanks in advance for any help.
const MealForm =(props) => {
return (
<ul className='mealForm'>
<MealItem meals={AvailableMeals} />
</ul>
)
}
const MealItem =(props) => {
// const mealsCtx = useContext(AvailableMeals);
const [cart, setCart] = useState(null)
const [curMeal, setCurMeal] = useState(null)
const addToCartHandler = (e, props) => {
e.preventDefault();
console.log(this); //This is what I am using to try and target
}
if ( props.meals.length === 0) {
return( <h2>No meals found!!</h2>)
} else{
return (
<Card >
{AvailableMeals.map((meal)=> {
return<>
<ul className='mealItem'>
<div className='mealItem-info'>
<li>{meal.name}</li>
<li>{meal.description}</li>
<li>{meal.price}</li>
<MealItemForm
id={meal.id}
key={meal.id}
name={meal.name}
description={meal.description}
onSub={addToCartHandler}
/>
</div>
</ul>
</>
})};
</Card>
)
}
};
const MealItemForm = (props) => {
return(
<form onSubmit={props.onSub}>
<Input label="Amount " input={{
id: 'amount_' + props.id,
type: 'number',
min: '1',
max: '10',
step: '1',
defaultValue: '1'
}}
/>
<button> + Add</button>
</form>
)
}
const Input = (props) => {
return (
<div >
<label htmlFor={props.input.id}>{props.label}</label>
<input {...props.input}/>
</div>
)
}
One is able to use stack-snippets to provide a working demo of one's code. This answer addresses the OP's question in trying to identify which meal's click event was invoked.
Changes significant to the question:
On the component: MealItemForm, changed below:
<form name={props.name} onSubmit={props.onSub}>
On the component: MealItem, changed addToCartHandler method:
console.log('mean-name corresponding to the clicked-item: ', e.target.name);
Impact observed:
The console.log properly displays the meal.name corresponding to the item's whose button was clicked.
Code Snippet
const {useState} = React;
const Input = (props) => {
return (
<div >
<label htmlFor={props.input.id}>{props.label}</label>
<input {...props.input}/>
</div>
)
};
const MealItemForm = (props) => {
return(
<form name={props.name} onSubmit={props.onSub}>
<Input label="Amount " input={{
id: 'amount_' + props.id,
type: 'number',
min: '1',
max: '10',
step: '1',
defaultValue: '1'
}}
/>
<button> + Add</button>
</form>
)
};
const MealItem = (props) => {
const [cart, setCart] = useState(null);
const [curMeal, setCurMeal] = useState(null);
const addToCartHandler = (e, props) => {
e.preventDefault();
console.log('mean-name corresponding to the clicked-item: ', e.target.name);
};
if ( props.meals.length === 0) {
return (<h2>No meals found!!</h2>)
} else {
return (
<div>
{
props.meals.map(meal => {
return (
<div>
<ul className='mealItem'>
<div className='mealItem-info'>
<li>{meal.name}</li>
<li>{meal.description}</li>
<li>{meal.price}</li>
<MealItemForm
id={meal.id}
key={meal.id}
name={meal.name}
description={meal.description}
onSub={addToCartHandler}
/>
</div>
</ul>
</div>
)
})
}
</div>
)
}
};
const AvailableMeals = [
{name: 'name 0', description: 'description 0', price: 'price 0', id: '0' },
{name: 'name 1', description: 'description 1', price: 'price 1', id: '1' },
{name: 'name 2', description: 'description 2', price: 'price 2', id: '2' },
{name: 'name 3', description: 'description 3', price: 'price 3', id: '3' },
{name: 'name 4', description: 'description 4', price: 'price 4', id: '4' }
];
const MealForm =(props) => {
return (
<ul className='mealForm'>
<MealItem meals={AvailableMeals} />
</ul>
)
};
ReactDOM.render(
<div>
DEMO
<MealForm />
</div>,
document.getElementById("rd")
);
.mealItem-info {
display: flex; border: 2px solid grey; width: fit-content;
align-items: center;
justify-content: space-around;
margin: 5px;
}
.mealItem-info>li { padding: 20px; margin-left: 25px; }
<div id="rd" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>

How to change specific item button name by clicking the button in ReactJs?

I have multiple item cards on a page, I want to change only a specific item/card button " Add Favt" to "Remove Favt" when the user clicks on the "Add Favt" button. But in my case, all of the card button names change when clicked on only one card button.
Here is my approach:
const Listitem = ({ posts }) => {
const [btn, setBtn] = useState('Add Favt');
var arr = [];
const click = (index) => {
arr.push(posts[index]);
console.log(arr);
localStorage.setItem('items', JSON.stringify({ arr }));
if (btn === 'Add Favt') {
setBtn('Remove Favt');
} else {
setBtn('Add Favt');
}
};
return (
<div className="fav-content">
<ul className="card">
{posts.map((item, index) => {
console.log(item._id);
return (
<li key={item._id}>
<button onClick={() => click(index)}>{btn}</button>
<div className="post">
<h1>Name: {item.name}</h1>
<p>Bio: {item.bio}</p>
<a href={item.link}>Link: {item.link}</a>
</div>
</li>
);
})}
</ul>
</div>
);
};
How to solve this problem?
This may be one possible solution to achieve the desired objective.
Code Snippet
Please view the snippet in Full Page
const {useState} = React;
const Listitem = ({posts, ...props}) => {
// track whether each card is is favorite or not
const [isFavorite, setIsFavorite] = useState({});
// when button clicked, flip card from favorite
const handleClick = e => {
const id = e.target.id;
setIsFavorite(prev => ({
...prev,
[id]: !prev[id]
}))
};
return (
<div className="fav-content">
List of favorites: {
posts
.filter(({_id}) => [_id] in isFavorite && isFavorite[_id])
.map(({name}) => name)
.join()
}
<ul className="card">
{posts.map(item => (
<li key={item._id}>
<button
id={item._id}
onClick={handleClick}
>
{
isFavorite[item._id]
? 'Remove Fav'
: 'Add Fav'
} {item.name}
</button>
<div className="post">
<h4>Name: {item.name}</h4>
<p>Bio: {item.bio}</p>
<a href={item.link}>Link: {item.link}</a>
</div>
</li>
)
)}
</ul>
</div>
);
};
const itemsList = [
{_id: 1, name: 'item 1', bio: 'bio 1', link: 'link 1'},
{_id: 2, name: 'item 2', bio: 'bio 2', link: 'link 2'},
{_id: 3, name: 'item 3', bio: 'bio 3', link: 'link 3'},
{_id: 4, name: 'item 4', bio: 'bio 4', link: 'link 4'},
];
ReactDOM.render(
<div>
DEMO
<Listitem posts={[...itemsList]}/>
</div>,
document.getElementById("rd")
);
.fav-content { width: fit-content; padding: 5px 15px; }
.card { background-color: #DDDDFF; margin: 5px 15px; }
.post { background-color: #FFFFDD; margin: 5px 15px; }
<div id="rd" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
Explanation
Inline comments have been provided in the above code snippet.
You are using btn state variable for each button. setting btn in state will reflect in all of them. Make a separate component for this.
<button onClick={() => click(index)}>{btn}</button>
<div className="post">
<h1>Name: {item.name}</h1>
<p>Bio: {item.bio}</p>
<a href={item.link}>Link: {item.link}</a>
</div>
Maintain a local state in new component for status of individual item.

React hook page throws 'filter' of undefined (anonymous) exception and unable to perform search

I have implemented a React hookhome page where I have got list of players displaying, I have added an input search to search the players based on the player name or Position, but it throws below exception,could someone please advise the cause of exception;
TypeError: Cannot read property 'filter' of undefined (anonymous
function) C:/Project1/soccerpep/src/components/Home.js:18 15 |
setSearchTerm(event.target.value); 16 | }; 17 | React.useEffect(()
=> {
18 | const results = playerList.name.filter(player =>
| ^ 19 | player.toLowerCase().includes(searchTerm) 20 | ); 21 | setSearchResults(results);
The react hook page follows below:
import React, { useEffect, useState } from "react";
import { Link } from 'react-router-dom';
var playerList = [
{ image: '/images/person.png', name: 'Player 1', position: "Forward" },
{ image: '/images/person.png', name: 'Player 2', position: "Defense" },
{ image: '/images/person.png', name: 'Player 3', position: "Mid Fielder" },
{ image: '/images/person.png', name: 'Player 4', position: "Forward" }
];
const Home = () => {
const [searchTerm, setSearchTerm] = React.useState("");
const [searchResults, setSearchResults] = React.useState([]);
const handleChange = event => {
setSearchTerm(event.target.value);
};
useEffect(() => {
const results = playerList.name.filter(player =>
player.toLowerCase().includes(searchTerm)
);
setSearchResults(results);
}, [searchTerm]);
return (
<div className="App">
<div className="wrapper">
<div className="playerList_header">
<h1>Players</h1>
<label>
<div className="playerSearch_Home">
<div className="playerSearch_Icon">
<img src="/images/search-image-player.jpg"></img>
</div>
<input type="text" className="playerSearch_Home_Input" placeholder="Search players..." value={searchTerm} onChange={handleChange}/>
</div>
</label>
</div>
<div className="playerList_home_page">
<div className="grid-container">
{
playerList.map(player => {
return (
<div className="grid-item">
<div>
<img className="playerProfilePic_home_tile" key={player.image} src={player.image}></img>
</div>
<div className="playerProfile_grid_border">
<h3 key={player.name}>{player.name}</h3>
<span className="playerPosition_home_tile" key={player.position}>{player.position}</span>
</div>
</div>
);
})
},
{
searchResults.map(player => {
return (
<div className="grid-item">
<div>
<img className="playerProfilePic_home_tile" key={player.image} src={player.image}></img>
</div>
<div className="playerProfile_grid_border">
<h3 key={player.name}>{player.name}</h3>
<span className="playerPosition_home_tile" key={player.position}>{player.position}</span>
</div>
</div>
);
})
}
</div>
</div>
</div>
</div>
);
}
export default Home;
playerList is an array, not an object, it doesn't have property name:
const results = playerList.filter(player =>
player.name.toLowerCase().includes(searchTerm) || player.position.toLowerCase().includes(searchTerm)
);
.filter() can be used only on arrays, you tried to run on name property which does not exist.
Instead you can use as the following:
var playerList = [
{ image: '/images/person.png', name: 'Player 1', position: "Forward" },
{ image: '/images/person.png', name: 'Player 2', position: "Defense" },
{ image: '/images/person.png', name: 'Player 3', position: "Mid Fielder" },
{ image: '/images/person.png', name: 'Player 4', position: "Forward" }
];
const searchTerm = 'Player 2';
const results = playerList.filter(p => p.name.toLowerCase() === searchTerm.toLowerCase());
console.log(results);
I hope this explains!

Fetch data from API when form's search button clicked and show data on another page in React JS

I am developing a React JS web application where I have a form with four select fields (Make, Model, Min price and Max price) and a Search button. The data for search results will be fetched from API according to the selection of options. I want to show that data on another page in a card (page route path: /search) when user clicked on search button. I am using react router. The API url/end point is https://mysterious-journey-51969.herokuapp.com/api/search-vehicle/?q=mercedes&m=sprinter&pf=0&pt=100000 where "q" field matches Vehicle Make, "m" field matches Model, "pf" field matches Min Price, "pt" field matches Max Price. How I can do that?
Here is my Form component code:
import React, { Component } from 'react';
import { Form, FormGroup, Input } from 'reactstrap';
import { veh_data } from '../shared/vehicle_make_and_models';
const defaultValues = [
{ value: 0, text: 0, key: 1 },
{ value: 500, text: 500, key: 2 },
{ value: 1000, text: 1000, key: 3 },
{ value: 1500, text: 1500, key: 4 },
{ value: 2000, text: 2000, key: 5 },
{ value: 2000, text: 2000, key: 6 }
];
const MIN_TITLE = { selected: true, disabled: true, text: 'Min Price' };
const MAX_TITLE = { selected: true, disabled: true, text: 'Max Price' };
class ImgAndForm extends Component {
constructor(props) {
super(props);
this.handleSearch = this.handleSearch.bind(this);
this.keyToOption = this.keyToOption.bind(this);
this.renderOptions = this.renderOptions.bind(this);
this.handleModelChange = this.handleModelChange.bind(this);
this.state = {
minData: [MIN_TITLE, ...defaultValues],
maxData: [MAX_TITLE, ...defaultValues],
minValue: null,
maxValue: null,
modelSelected: null
};
}
renderOptions(data) {
return data.map(datum => {
// this allows us to indicate whether we are selecting or disabling
const selected = datum.selected || false;
const disabled = datum.disabled || false;
return (
<option key={datum.key} value={datum.value} selected={selected} disabled={disabled}>
{datum.text}
</option>
);
});
}
handleModelChange(event) {
console.log(event.target.value);
this.setState({ modelSelected: event.target.value });
}
handleSearch(event) {
alert("Search button clicked");
}
keyToOption(key) {
return key.split("-")
.map(word => word.slice(0, 1).toUpperCase() + word.slice(1))
.join(" ");
}
handleMinSelect = event => {
const value = event.target.value;
const newMaxValues = [];
defaultValues.forEach(datum => {
if (datum.value >= Number.parseInt(value, 10)) {
newMaxValues.push(datum);
}
});
this.setState({
maxData: [MAX_TITLE, ...newMaxValues],
minValue: value
});
};
handleMaxSelect = event => {
const value = event.target.value;
this.setState({ maxValue: value });
};
render() {
const vehicles = veh_data.reduce((acc, veh, i) => {
let make = Object.keys(veh)[0],
vehModels = veh[make];
return {
makes: [
...acc.makes,
<option key={make + i} value={make}>{this.keyToOption(make)}</option>
],
models: {
...acc.models,
[make]: vehModels.map((model, i) => {
return (
<option key={make + model + i} value={model}>
{this.keyToOption(model)}
</option>
);
})
}
};
}, { makes: [], models: [] });
const selectedModels =
this.state.modelSelected && this.state.modelSelected.length ? (
vehicles.models[this.state.modelSelected]
) : (
<option value="">Model (select make first)</option>
);
return (
<div>
<header className="headerbg d-flex">
<div className="container my-auto">
<div className="row">
<div className="offset-1 col-10 offset-lg-0 col-lg-4">
<div id="search-form-div" className="container">
<div className="row">
<div className="col-12 my-4">
<h3>Search</h3>
<Form onSubmit={this.handleSearch}>
<FormGroup>
<Input
onChange={e => this.handleModelChange(e)}
type="select"
name="q"
id="q"
>
<option value="">Make</option>
{vehicles.makes}
</Input>
</FormGroup>
<FormGroup>
<Input type="select" name="m" id="m">
{selectedModels}
</Input>
</FormGroup>
<FormGroup>
<Input type="select"
name="pf"
id="pf"
value={this.state.minValue}
onChange={this.handleMinSelect}>
{this.renderOptions(this.state.minData)}
</Input>
</FormGroup>
<FormGroup>
<Input
type="select"
name="pt"
id="pt"
value={this.state.maxValue}
onChange={this.handleMaxSelect}>
{this.renderOptions(this.state.maxData)}
</Input>
</FormGroup>
<FormGroup>
<Input type="submit" name="search" id="search" className="btn btn-primary" value="Search" />
</FormGroup>
</Form>
</div>
</div>
</div>
</div>
</div>
</div>
</header>
</div>
);
}
}
export default ImgAndForm;
Here is my Search result component code:
import React, { Component } from 'react';
import Smallheader from './SmallHeader';
import { Card, CardImg, CardTitle, CardSubtitle } from 'reactstrap';
class SearchResult extends Component {
constructor(props) {
super(props);
this.state = {
};
}
render() {
return (
<div>
<Smallheader />
<div className="my-5">
<div className="container text-center" id="contactContainer">
<div className="row">
<div className="col-lg-12 mx-auto">
<h2 className="text-center">Search Results</h2>
<hr className="my-4 thick-hr" />
</div>
</div>
<div className="row">
<div className="col-6 col-lg-3 mt-4">
<Card>
<a href="#">
<CardImg src="" className="img-fluid" />
<CardTitle>Title Here</CardTitle>
<CardSubtitle>Price Here</CardSubtitle>
</a>
</Card>
</div>
</div>
</div>
</div>
</div>
);
}
}
export default SearchResult;
Here is a working solution...
https://codesandbox.io/s/lrv2w3qxlq?moduleview=1
I've imported your SearchResults component and put it directly below your ImgAndForm, but you can move it anywhere in that render function.
For this specific situation you would need a way to render this on a new 'page' you would need a way to manage shared application state, like Redux or at least a container component as #MikeZinn mentioned, but to do that properly would require as significant amount of work to implement the routing and re-architect your entire program. (If you want I can show you a small hack to produce the same result without that for now, but I'd advise looking into a more permanent solution.)
Since the SearchResults component can be 'stateless' I removed the constructor function, but I left it as a class for now because this component will likely need state eventually.
I added the axios library to fetch the data from the API, but any other XHR module already used in your program will do.
NOTE: Since the specific API endpoints that your form is currently able to query are unavailable, I've hard coded the 'mercedes' example you provided, but the program will log both 'realQuery' and 'dummyQuery' so you see that it is producing the correct query structure for whenever you fix that.
Form Component
import React, { Component } from "react";
import { Form, FormGroup, Input } from "reactstrap";
// import { veh_data } from '../shared/vehicle_make_and_models';
import SearchResult from "./result";
import axios from "axios";
const veh_data = [
{ "alfa-romeo": ["145", "90", "Alfa 6", "Alfasud"] },
{ "aston-martin": ["15", "2-Litre", "AM Vantage", "Atom", "Cygnet", "DB2"] },
{ audi: ["100", "200", "A1", "A2", "A3", "A4", "A5", "A6", "A7"] }
];
const defaultValues = [
{ value: 0, text: 0, key: 1 },
{ value: 500, text: 500, key: 2 },
{ value: 1000, text: 1000, key: 3 },
{ value: 1500, text: 1500, key: 4 },
{ value: 2000, text: 2000, key: 5 },
{ value: 2000, text: 2000, key: 6 }
];
const MIN_TITLE = { selected: true, disabled: true, text: "Min Price" };
const MAX_TITLE = { selected: true, disabled: true, text: "Max Price" };
class ImgAndForm extends Component {
constructor(props) {
super(props);
this.handleSearch = this.handleSearch.bind(this);
this.keyToOption = this.keyToOption.bind(this);
this.renderOptions = this.renderOptions.bind(this);
this.handleModelChange = this.handleModelChange.bind(this);
this.state = {
minData: [MIN_TITLE, ...defaultValues],
maxData: [MAX_TITLE, ...defaultValues],
minValue: "",
maxValue: "",
modelSelected: "",
makeSelected: "",
searchResults: ""
};
}
renderOptions(data) {
return data.map(datum => {
// this allows us to indicate whether we are selecting or disabling
const selected = datum.selected || false;
const disabled = datum.disabled || false;
return (
<option
key={datum.key}
value={datum.value}
selected={selected}
disabled={disabled}
>
{datum.text}
</option>
);
});
}
handleModelChange(event) {
console.log(event.target.value);
this.setState({ modelSelected: event.target.value });
}
handleMakeChange(event) {
console.log(event.target.value);
this.setState({ makeSelected: event.target.value });
}
async handleSearch(event) {
event.preventDefault();
alert("Search button clicked");
let { makeSelected, modelSelected, minValue, maxValue } = this.state;
let realQuery =
"https://mysterious-journey-51969.herokuapp.com/api/search-vehicle/?" +
`q=${makeSelected.split("-").join("")}` +
`&m=${modelSelected.split("-").join("")}` +
`&pf=${minValue}` +
`&pt=${maxValue}`;
let dummyQuery =
"https://mysterious-journey-51969.herokuapp.com/api/search-vehicle/?q=mercedes&m=sprinter&pf=0&pt=100000";
console.log("realQuery (was not run)", realQuery);
console.log("dummyQuery (was run)", dummyQuery);
let res = await axios.get(dummyQuery).catch(err => console.log(err));
console.log("res", res.data);
if (res && res.data) {
this.setState(prevState => {
return {
...prevState,
searchResults: res.data
};
});
}
}
keyToOption(key) {
return key
.split("-")
.map(word => word.slice(0, 1).toUpperCase() + word.slice(1))
.join(" ");
}
handleMinSelect = event => {
const value = event.target.value;
const newMaxValues = [];
defaultValues.forEach(datum => {
if (datum.value >= Number.parseInt(value, 10)) {
newMaxValues.push(datum);
}
});
this.setState({
maxData: [MAX_TITLE, ...newMaxValues],
minValue: value
});
};
handleMaxSelect = event => {
const value = event.target.value;
this.setState({ maxValue: value });
};
render() {
const vehicles = veh_data.reduce(
(acc, veh, i) => {
let make = Object.keys(veh)[0],
vehModels = veh[make];
return {
makes: [
...acc.makes,
<option key={make + i} value={make}>
{this.keyToOption(make)}
</option>
],
models: {
...acc.models,
[make]: vehModels.map((model, i) => {
return (
<option key={make + model + i} value={model}>
{this.keyToOption(model)}
</option>
);
})
}
};
},
{ makes: [], models: [] }
);
const selectedModels =
this.state.makeSelected && this.state.makeSelected.length ? (
vehicles.models[this.state.makeSelected]
) : (
<option value="">Model (select make first)</option>
);
return (
<div>
<header className="headerbg d-flex">
<div className="container my-auto">
<div className="row">
<div className="offset-1 col-10 offset-lg-0 col-lg-4">
<div id="search-form-div" className="container">
<div className="row">
<div className="col-12 my-4">
<h3>Search</h3>
<Form onSubmit={this.handleSearch}>
<FormGroup key={1}>
<Input
onChange={e => this.handleMakeChange(e)}
type="select"
name="q"
id="q"
>
<option value="">Make</option>
{vehicles.makes}
</Input>
</FormGroup>
<FormGroup key={2}>
<Input
onChange={e => this.handleModelChange(e)}
type="select"
name="m"
id="m"
>
{selectedModels}
</Input>
</FormGroup>
<FormGroup key={3}>
<Input
type="select"
name="pf"
id="pf"
value={this.state.minValue}
onChange={this.handleMinSelect}
>
{this.renderOptions(this.state.minData)}
</Input>
</FormGroup>
<FormGroup key={4}>
<Input
type="select"
name="pt"
id="pt"
value={this.state.maxValue}
onChange={this.handleMaxSelect}
>
{this.renderOptions(this.state.maxData)}
</Input>
</FormGroup>
<FormGroup key={5}>
<Input
type="submit"
name="search"
id="search"
className="btn btn-primary"
value="Search"
/>
</FormGroup>
</Form>
<SearchResult results={this.state.searchResults} />
</div>
</div>
</div>
</div>
</div>
</div>
</header>
</div>
);
}
}
export default ImgAndForm;
Results Component
import React, { Component } from "react";
// import Smallheader from './SmallHeader';
import { Card, CardImg, CardTitle, CardSubtitle } from "reactstrap";
class SearchResult extends Component {
renderResults() {
let { results } = this.props;
console.log("results", results);
if (results && results.length) {
return results.map(({ price, text, title, remote_image }, i) => {
return (
<Card key={"card-" + i}>
<a href="#">
<CardImg src={remote_image} className="img-fluid" />
<CardTitle>{title}</CardTitle>
<CardSubtitle>{price}</CardSubtitle>
</a>
</Card>
);
});
}
}
render() {
return (
<div>
{/* <Smallheader /> */}
<div className="my-5">
<div className="container text-center" id="contactContainer">
<div className="row">
<div className="col-lg-12 mx-auto">
<h2 className="text-center">Search Results</h2>
<hr className="my-4 thick-hr" />
</div>
</div>
<div className="row">
<div className="col-6 col-lg-3 mt-4">{this.renderResults()}</div>
</div>
</div>
</div>
</div>
);
}
}
export default SearchResult;
This is exactly the type of problem Redux Solves without using Redux you will need to store the state on a shared parent component. For example,
class Search extends Component {
state = {
searchResult: null,
};
handleSearch = searchResult => {
this.setState({
searchResult,
});
}
render(){
const { searchResult, } = this.state;
if(searchResult === null){
return (
<ImgAndForm handleSearch={this.handleSearch} />
);
}
return (
<SearchResult searchResult={searchResult} />
);
}
}

Categories