React: trigger event in child component by click on parent - javascript

Context:
I want to trigger an event in a parents child component by an onClick on the parent element
Code:
Parent PlantContainer:
import React from "react";
import ClipLoader from "react-spinners/ClipLoader";
import Box from '#material-ui/core/Box';
import ShowMetric from '../showMetric';
export default class PlantContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading: false,
};
}
render() {
return (
<Box>
<h2>{this.props.plantName}</h2>
<ShowMetric
setting={this.props.plantName + ".moisture"}
unit="%">Moisture:</ShowMetric>
<ShowMetric
setting={this.props.plantName + ".conductivity"}
unit="%">Fertility:</ShowMetric>
</Box>
);
}
}
Child ShowMetric:
import React from "react";
import ClipLoader from "react-spinners/ClipLoader";
import resolvePath from 'object-resolve-path';
export default class ShowMetric extends React.Component {
constructor(props) {
super(props);
this.getData = this.getData.bind(this);
this.state = {
isLoading: false,
reading: 0,
};
}
getData() {
this.setState({ isLoading: true });
fetch(URL_HERE, {
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "*",
},
})
.then(function (response) {
return response.json();
})
.then((json) =>
this.setState({
reading: resolvePath(json, this.props.setting),
isLoading: false,
})
);
}
componentDidMount() {
this.getData();
}
render() {
if (this.state.isLoading) {
return <ClipLoader />;
}
return (
<div onClick={this.getData}>
{this.props.children + " "}
<nobr>{`${this.state.reading.toFixed(1)} ${this.props.unit}`}</nobr>
</div>
);
}
}
Main App.js:
import './App.css';
import React from 'react';
import Container from '#material-ui/core/Container';
import Box from '#material-ui/core/Box';
import PlantContainer from './components/plantContainer';
function App() {
return (
<div className="App">
<Container maxWidth="md">
<Box className="flexBox">
<PlantContainer plantName="Plant_1"/>
<PlantContainer plantName="Plant_2"/>
</Box>
</Container>
</div>
);
}
export default App;
Problem
The above code works as expected, as <ShowMetric/> shows the information and reloads when I click on it.
Now I want to reload all <ShowMetric/> Elements in PlantContainer (maybe trigger the getData() function for each of them) when I click the <H2> Element of PlantContainer.
I tried to find ways how to pass down events or informations to children, but since props can't change at runtime (?) and I don't think a reference would be the best way here, I am a bit at lost on how to implement this.
And as this is my very first react web App and endeavour into this framework please call out any fishy thing you can find in the code.

I think the more elegant way to do this would be to store all the data in the parent component and pass it down to the children through the props.
Here is a possible solution (I used function components as it should be privileged over the class components) :
PlantContainer
function fetchData() {
return fetch(URL_HERE, {
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "*",
},
})
.then(response => response.json());
}
export default function PlantContainer(props) {
const [data, setData] = React.useState({
isLoading: false,
'moisture': 0,
'conductivity': 0
});
function loadData() {
setData({...data, isLoading: true});
fetchData().then(json => {
setData({
isLoading: false,
'moisture': resolvePath(json, `${props.plantName}.moisture`),
'conductivity': resolvePath(json, `${props.plantName}.conductivity`)
});
});
}
React.useEffect(loadData, []);
return (
<Box>
<h2 onClick={loadData}>{props.plantName}</h2>
{data.isLoading && <ClipLoader/>}
{!data.isLoading && (
<ShowMetric
reading={data['moisture']}
unit="%">Moisture:</ShowMetric>
<ShowMetric
reading={data['conductivity']}
unit="%">Fertility:</ShowMetric>
)}
</Box>
);
}
ShowMetric
export default function ShowMetric(props) {
return (
<div>
{props.children + " "}
<nobr>{`${props.reading.toFixed(1)} ${props.unit}`}</nobr>
</div>
);
}
As you can retrieve all the data by calling the service a single time, it seems to be useless to reload only one metric, so I only give to opportunity to reload both metrics by clicking on the h2 element.

The useImperativeHandle hook is perfect to allow child components and refs.
Fully working example with Typescript support too!:
//Child Component
//Create your ref types here
export type RefHandler = {
pressAlert: () => void;
inputRef: RefObject<HTMLInputElement>;
};
const Child = forwardRef<RefHandler, Props>((props, ref) => {
const submitRef = useRef<HTMLButtonElement>(null);
const inputRef = useRef<HTMLInputElement>(null);
//Initialise your refs here
useImperativeHandle(ref, () => ({
inputRef: inputRef,
pressAlert: () => submitRef?.current?.click()
}));
return (
<div>
<p>Child Component</p>
<input type="text" value="lorem ipsum" ref={inputRef} />
<br />
<button onClick={() => alert("Alert pressed")} ref={submitRef}>
Alert
</button>
</div>
);
});
//Parent
export default function Parent() {
const childRef = useRef<RefHandler>(null);
return (
<>
<p>Parent</p>
<button
onClick={() => {
alert(childRef?.current?.inputRef?.current?.value);
}}
>
Read child input
</button>
<button onClick={() => childRef?.current?.pressAlert()}>
Press child button
</button>
<hr />
<Child ref={childRef} />
</>
);
}

Related

How to set a variable in a parent component from a child component

I have what I call a parent component. It is a modal. I am trying to update a variable from a child component, namely the ShippingBlock. I am trying to update shippingMethod.
import React from 'react';
import "./Checkout.scss"
import AddressBlock from './AddressBlock';
import PaymentBlock from './PaymentBlock';
import ShippingBlock from './ShippingBlock';
import TotalsBlock from './TotalsBlock';
import { Modal, ModalHeader, ModalBody } from 'reactstrap';
class CheckoutModal extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'React'
};
}
render() {
let shippingMethod = null;
function setShippingMethod(method) {
shippingMethod = method;
}
const buttonStyles = {
position: "absolute",
top: "-35px",
right: "10px",
border: "0",
background: "none"
};
return (
<Modal id="checkout"
isOpen
size='xl'>
<button onClick={this.props.onRequestClose} style={buttonStyles}>x</button>
<ModalHeader className="header">
Checkout
</ModalHeader>
<ModalBody>
<div className="row">
<AddressBlock />
<ShippingBlock setShippingMethod={setShippingMethod} shippingMethod={shippingMethod}/>
</div>
<div className="row">
<PaymentBlock />
<TotalsBlock />
</div>
</ModalBody>
</Modal>
);
}
}
export default CheckoutModal;
import React from 'react';
import Config from 'config';
import "./Checkout.scss"
class ShippingBlock extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'React',
shippingMethods: [],
defaultMethod: this.props.shippingMethod
};
}
async componentDidMount() {
console.log('Running componentDidMount')
const tokenString = sessionStorage.getItem("token");
const token = JSON.parse(tokenString);
let headers = new Headers({
"Accept": "application/json",
"Content-Type": "application/json",
'Authorization': 'Bearer ' + token.token
});
const response = await fetch(Config.apiUrl + `/api/Shipping/GetShippingMethods`, {
method: "GET",
headers: headers
});
const json = await response.json();
console.log(json);
this.setState({ shippingMethods: json });
if (this.state.defaultMethod === null) {
const mresponse = await fetch(Config.apiUrl + `/api/Users/GetDefaultShippingMethod`, {
method: "GET",
headers: headers
});
const mjson = await mresponse.json();
console.log(mjson);
this.setState({ defaultMethod: mjson });
}
}
setShippingMethod(e) {
console.log('Shipping method ' + JSON.stringify(e.target.value));
this.props.setShippingMethod(e.target.value);
this.state.defaultMethod = e.target.value;
}
render() {
const shippingMethods = this.state.shippingMethods;
const defaultMethod = this.state.defaultMethod;
return (
<aside id="checkout" className="block col-1">
<h1>Shipping</h1>
<div className="row">
<select id="comboMethod" onChange={this.setShippingMethod.bind(this)} value={defaultMethod === null ? null : defaultMethod.trim()}>
{shippingMethods.map(method => { return <option key={method.shipmthd.trim()} value={method.shipmthd.trim()}>{method.shipmthd.trim()}</option>} )}
</select>
</div>
</aside>
);
}
}
export default ShippingBlock;
Whenever I try to change the select in ShippingBlock my function onChange runs and the console log shows the correct selected value, but somehow the value gets set right back to the default. I haven't been able pinpoint what I have done wrong. Any tips or help would be appreciated.
UPDATE
import React from 'react';
import "./Checkout.scss"
import AddressBlock from './AddressBlock';
import PaymentBlock from './PaymentBlock';
import ShippingBlock from './ShippingBlock';
import TotalsBlock from './TotalsBlock';
import { Modal, ModalHeader, ModalBody } from 'reactstrap';
class CheckoutModal extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'React',
shippingMethod: null
};
}
setShippingMethod(method) {
this.state.shippingMethod = method;
}
render() {
const buttonStyles = {
position: "absolute",
top: "-35px",
right: "10px",
border: "0",
background: "none"
};
return (
<Modal id="checkout"
isOpen
size='xl'>
<button onClick={this.props.onRequestClose} style={buttonStyles}>x</button>
<ModalHeader className="header">
Checkout
</ModalHeader>
<ModalBody>
<div className="row">
<AddressBlock />
<ShippingBlock setShippingMethod={this.setShippingMethod.bind(this)} shippingMethod={this.state.shippingMethod}/>
</div>
<div className="row">
<PaymentBlock />
<TotalsBlock />
</div>
</ModalBody>
</Modal>
);
}
}
export default CheckoutModal;
This is what I had before. It behaves the same.
What you've done wrong is that shippingMethod is not part of the parent component state.
You have defined it inside the render function, which runs everytime the parent component is rendered. This means that the value you set in there will reset back to what it was previously before the child is rendered i.e. null, therefore the child component will get the null when it is rendered.
Solution
Move the variable into the parent's state, and make the setter a member of the parent class.
Your setShippingMethod should be using this.setState (similar to the useState hook in functional components):
setShippingMethod(method) {
this.setState(state => ({...state, shippingMethod: method}));
}

How to prop an input value into a parameter for an API call

I have a Header.js component that takes a user's input and updates its state. I want to transfer(prop?) this item into the parent component App.js where it will be put in as a parameter and the data will be console logged relative to the user's input. I don't know how to transfer the state and implement it into the API's parameter.
class Header extends Component {
constructor() {
super();
this.state = {
query: '',
}
}
handleSubmit = (e) => {
e.preventDefault();
// form's input value
let userSearch = this.state.query;
}
handleChange = (e) => {
this.setState({
query: e.target.value
});
}
render() {
return (
<header>
<form onSubmit={this.handleSubmit}>
<input
onChange={this.handleChange}
type="text"
placeholder="Search"
/>
<label className="sr-only" htmlFor="search">Search News</label>
</form>
</header>
)
}
}
export default Header
import Header from './Components/Header'
import axios from 'axios';
class App extends Component {
constructor() {
super();
this.state = {
articles: [],
}
}
componentDidMount() {
axios({
// I want this.state.query in header.js to be {parameter}
url: 'http://newsapi.org/v2/everything?q={parameter}&sortBy=popularity&apiKey=where-the-key-goes',
method: 'GET',
responseType: 'JSON',
}).then((response => {
let articles = response.data.articles;
this.setState({
articles,
isLoading: false,
})
console.log(articles);
}))
}
render() {
return (
<>
<Header />
</>
)
}
}
export default App
You could create a callback function in the App component and pass to Header as a prop:
class App extends Component {
...
handleSearch = (value) => {
axios({
url: `http://newsapi.org/v2/everything?q=${value}&sortBy=popularity&apiKey=where-the-key-goes`,
method: "GET",
responseType: "JSON",
}).then((response) => { ... });
};
render() {
return (
<>
<Header handleSearch={this.handleSearch} />
</>
);
}
}
Then use it in the Header's handleSubmit function:
handleSubmit = (e) => {
e.preventDefault();
// form's input value
let userSearch = this.state.query;
this.props.handleSearch(userSearch);
};
class Header extends Component<Props> { // add Props
...
handleSubmit = (e) => {
e.preventDefault();
// form's input value
let userSearch = this.state.query;
this.props.onValueSet(userSearch); // callback
}
...
}
class App extends Component {
...
// add callback
_setValueHandle = (value) => {
console.log(value);
this.setState({parameter: value}); // do something u want
};
render() {
return (
<>
{/* set callback */}
<Header onValueSet={this._setValueHandle} />
</>
)}
...
}
how about this?
You have to add props from App.js. Also you don't need to call the api on componentDidMount because you want have the query yet. Try this:
class Header extends Component {
static defaultProps = {
onUpdate: () => {},
onSubmission: () => {}
}
constructor() {
super();
this.state = {
query: '',
}
}
handleSubmit = (e) => {
e.preventDefault();
// form's input value
let userSearch = this.state.query;
this.props.onSubmission(this.state.query); //Send submission to parent
}
handleChange = (e) => {
this.setState({
query: e.target.value
}, () => {
this.props.onUpdate(this.state.query); //Send change to parent
});
}
render() {
return (
<header>
<form onSubmit={this.handleSubmit.bind(this)}>
<input
onChange={this.handleChange.bind(this)}
type="text"
placeholder="Search"
/>
<label className="sr-only" htmlFor="search">Search News</label>
</form>
</header>
)
}
}
export default Header
import Header from './Components/Header'
import axios from 'axios';
class App extends Component {
constructor() {
super();
this.state = {
articles: [],
query: ""
}
}
componentDidMount() {
}
request(query){
axios({
// I want this.state.query in header.js to be {parameter}
//NOTE: Query could also be this.state.query since we're updating it on change
url: 'http://newsapi.org/v2/everything?q={parameter}&sortBy=popularity&apiKey=where-the-key-goes',
method: 'GET',
responseType: 'JSON',
}).then((response => {
let articles = response.data.articles;
this.setState({
articles,
isLoading: false,
})
console.log(articles);
}))
}
render() {
return (
<>
<Header onUpdate={query => this.setState({query: query})} onSubmission={this.request.bind(this)} />
</>
)
}
}
export default App

React - Show loader on Click that already has function assigned to it

I have already a a click event within a ternary operator that does a GET request from my API. When the button is clicked, the button disappears and the data text replaces the button (button disappears). But there is a small gap of time between the get request and the text reveal. I want to put a loading message of some kind at that moment of time so the user knows something is happening. But can't seem to figure it out. Here is my code:
import React, {Component} from "react";
import axios from "axios";
export default class FoodData extends React.Component {
constructor(props) {
super(props);
this.state = {
meal: '',
clicked: false,
isLoaded: false,
}
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({
clicked: true,
});
}
fetchData() {
axios.get('api/menu/food')
.then(res => {
const meal= `${res.data.starters},${ res.data.price}`;
this.setState({
meal: meal,
isLoaded: true
})
console.log(meal)
})
};
combinedFunction() {
this.fetchData();
this.handleClick();
}
render(){
const {isLoaded, meal} = this.state;
return (
<div >
Dish: {
this.state.clicked ? (
this.state.menu
) : (
<button onClick={() => { this.combinedFunction() }}>Find Dish</button>
)}
</div>
);
}
}
Appreciate the help.
What you can do is add a "isLoading" state and put the values before and after your API call like so:
fetchData() {
this.setState({isLoading: true});
axios.get('api/menu/food')
.then(res => {
const meal= `${res.data.starters},${ res.data.price}`;
this.setState({
meal: meal,
isLoaded: true
isLoading: false,
})
console.log(meal)
})
};
And use that on your render to show the "loading icon"
render(){
const {isLoaded, meal, isLoading } = this.state;
return (
<div >
{isLoading ? <div>loading</div> :
Dish: {
this.state.clicked ? (
this.state.menu
) : (
<button onClick={() => { this.combinedFunction() }}>Find Dish</button>
)}
}
</div>
);
}
}
This is a working demo which shows loading when api call starts and disables button to prevent multiple api calls. I added a 2sec time out to show the demo. Check the stackblitz sample
This is the updated code, here I used a fake api (https://jsonplaceholder.typicode.com/users) to show the demo
import React, {Component} from "react";
import axios from "axios";
export default class FoodData extends React.Component {
constructor(props) {
super(props);
this.state = {
meal: '',
clicked: false,
isLoaded: false,
}
this.handleClick = this.handleClick.bind(this);
this.combinedFunction = this.combinedFunction.bind(this)
}
handleClick() {
this.setState({
clicked: true,
});
}
fetchData() {
axios.get('https://jsonplaceholder.typicode.com/users')
.then(res => {
this.setState({
meal: res.data,
isLoaded: false
})
})
};
combinedFunction =()=> {
this.setState({isLoaded: true})
setTimeout(()=>{
this.fetchData();
},2000)
this.handleClick();
}
render(){
const {isLoaded, meal} = this.state;
return (
<>
<div >
Users:
<button onClick={this.combinedFunction } disabled={isLoaded ? true : false}>{isLoaded ? 'Loading...':'Find User'}</button>
</div>
<div>
{meal && meal.map(item =>(
<div key={item.id}>
<p>{item.id} - {item.name}</p>
</div>
))}
</div>
</>
);
}
}

React <Redirect> after transition not working

I have the following component which has a redirection route after an animation is finished, like so:
Menus.jsx
class Menus extends Component{
constructor (props) {
super(props);
this.state = {
select: 'espresso',
isLoading: false,
redirect: false
};
gotoCoffee = () => {
this.setState({isLoading:true})
setTimeout(()=>{
this.setState({isLoading:false,redirect:true})
},5000) //Replace this time with your animation time
}
renderCoffee = () => {
if (this.state.redirect) {
return (<Redirect to={`/coffee/${this.state.select}`} />)
}
}
render(){
return (
<div>
<h1 className="title is-1"><font color="#C86428">Menu</font></h1>
<hr/><br/>
<div>
{this.state.isLoading && <Brewing />}
{this.renderCoffee()}
<div onClick={this.gotoCoffee}
style={{textDecoration:'underline',cursor:'pointer'}}>
<strong><font color="#C86428">{this.state.coffees[0]}</font></strong></div>
</div>
</div>
);
}
}
export default withRouter(Menus);
The animation called onCLick:
Brewing.jsx
import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
import './css/mug.css'
class Brewing extends Component {
constructor (props) {
super(props);
};
render() {
return (
<div>
<div className="cup">
<div className="coffee"></div>
</div>
<div className="smoke"></div>
</div>
);
}
}
export default withRouter(Brewing);
And here redirected route component:
Coffee.jsx
class Coffees extends Component{
constructor (props) {
super(props);
this.state = {
select:'',
template:''
};
};
componentDidMount() {
if (this.props.isAuthenticated) {
this.getCoffee();
}
};
getCoffee(event) {
//const {userId} = this.props
const options = {
url: `${process.env.REACT_APP_WEB_SERVICE_URL}/coffee/espresso`,
method: 'get',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${window.localStorage.authToken}`
}
};
return axios(options)
.then((res) => {
console.log(res.data.data)
this.setState({
template: res.data.data[0].content
})
})
.catch((error) => { console.log(error); });
};
render(){
var __html = this.state.template;
var template = { __html: __html };
return (
<div id="parent">
<h1 className="title is-1"><font color="#C86428">{this.state.select} playlist</font></h1>
<hr/><br/>
<div dangerouslySetInnerHTML={template}/>
</div>
);
}
}
export default withRouter(Coffees);
but <Redirect> in Menus.jsx is not working....url changes at browser but nothing happens; only if I refresh the browser /coffee is sucessfully mounted.
What I actually need to happen:
render Menu
click on a link
click renders an animation
when animation is done, after 5 seconds,
<Redirect> to /coffee
what am I missing?
When you say url changes at browser but nothing happens; only if I refresh the browser /coffee is sucessfully mounted.
This might be the issue with your Routes.
When you redirect to path /coffee/${this.state.select}, you should have Route to handle this path.
<Route path="/coffee/:select?" render={() => ( <Coffees isAuthenticated={true}/> )}/>
Note: Be aware of adding exact prop to Route. When you add exact prop it means your path should match exactly with all the provided params.
You need to call getCoffee function in also componentDidUpdate function.
componentDidMount() {
if (this.props.isAuthenticated) {
this.getCoffee();
}
};
componentDidUpdate() {
if (this.props.isAuthenticated) {
this.getCoffee();
}
};
Your Redirect should be inside the render().
render(){
if(this.state.redirect) {
return(<Redirect to={`/coffee/${this.state.select}`} />)
} else {
return (
<div>
...your component
</div> );
}
}
Note that this way you shouldn't need your renderCoffee() function.
I'm on mobile so i wasn't able to test if it works. Let me know if this works for you.
It seems your Menu component construtor has no closing bracket.
...
class Menus extends Component{
constructor (props) {
super(props);
this.state = {
select: 'espresso',
isLoading: false,
redirect: false
};
} // did you miss this?
gotoCoffee = () => {
...

ReactJs setState skips first letter

Whenever i type something is search bar update the state, it skips the first letter. For example, if i write "asdf" it only shows "sdf".
I tried console.log before this line of code
this.props.newQuery(this.state.newSearchQuery);
and everything was working fine.
Please check the below code of App.js and SearchBar.js
Thanks
App.js
import React from 'react';
import SearchBar from './components/SearchBar';
class App extends React.Component {
constructor(){
super();
this.state = {
searchQuery: '',
fetchedData: []
};
}
newQuery(query){
this.setState({
searchQuery: query
});
}
onSearch(){
const userInput = this.state.searchQuery;
if(userInput !== '' && userInput !== ' '){
const API_KEY = `https://pokeapi.co/api/v2/pokemon/${userInput}`;
fetch(API_KEY, {
method: 'GET',
headers: {
Accept: 'application/json'
}
})
.then(result => result.json())
.then(data => this.setState({ fetchedData: data.results }));
console.log('res', this.state.fetchedData);
}
}
render(){
return(
<div className="App">
<h2>Search Pokemos by Types</h2>
<hr />
<SearchBar onSearch={this.onSearch.bind(this)} newQuery={this.newQuery.bind(this)} />
</div>
);
}
}
export default App;
SearchBar.js
import React from 'react';
class SearchBar extends React.Component {
constructor(props){
super(props);
this.state = {
newSearchQuery: '' //this blank value get executed first when i console.log
}
}
searchInput(event){
this.setState({
newSearchQuery: event.target.value
});
this.props.newQuery(this.state.newSearchQuery);
console.log(this.state.newSearchQuery); // if i log "asdf", state on top "newSearchQuery" skips the first letter, a and shows "sdf" only.
}
render(){
return(
<div className="input-group">
<input onChange={this.searchInput.bind(this)} className="form-control" placeholder="[eg. Ditto, Cheri, etc]" />
<button onClick={this.props.onSearch} className="btn btn-success">Search</button>
</div>
);
}
}
export default SearchBar;
The console.log doesnot log as expected because the setState() method is not always executed as its called. According to Docs
State Updates May Be Asynchronous:Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state.
So when you are logging this console.log(this.state.newSearchQuery); after the setState the state is not actually changed so thats why it logs unexpected
I'm a bit confused as to the redundant state between the two components. What I think I would do (if I wasn't using something like mobx) is keep the state on the parent component and pass the handleChange and handleSearch functions to the <Search /> component. It would look something like...
I threw together a codesandbox for you: https://codesandbox.io/s/n1ryz4rzwl
APP Component
import React, { Component } from 'react';
import SearchBar from './components/SearchBar'
class App extends Component {
constructor() {
super()
this.state = {
searchQuery: '',
fetchedData: []
}
}
handleChangeQuery = event => this.setState({searchQuery: event.target.value})
handleSearch = () => {
const {searchQuery} = this.state,
API_KEY = `https://pokeapi.co/api/v2/pokemon/${searchQuery}`;
fetch(API_KEY, {
method: 'GET',
headers: {
Accept: 'application/json'
}
})
.then(result => result.json())
.then(data => this.setState({ fetchedData: data.results }));
}
render() {
const {searchQuery} = this.state
return (
<div className="App">
<h2>Search Pokemos by Types</h2>
<hr />
<SearchBar
value={searchQuery}
handleChangeQuery={this.handleChangeQuery}
handleSearch={this.handleSearch}
/>
// Show fetchedData results here
</div>
)
}
}
export default App
SearchBar Component - This COULD be a stateless functional component
import React from 'react'
const SearchBar = ({value, handleChangeQuery, handleSearch}) => {
return (
<div className="input-group">
<input
onChange={handleChangeQuery}
value={value}
className="form-control"
placeholder="[eg. Ditto, Cheri, etc]"
/>
<button onClick={handleSearch} className="btn btn-success">Search</button>
</div>
)
}
export default SearchBar
The reasoning behind the strange missing character has been described by the other comments - as the this.setState() may be async. However, this.setState() does have a callback function that can be used to confirm the change if you want to test with that. It looks something like:
this.setState({key: value}, () => {
// State has been set
console.log(this.state.key)
})

Categories

Resources