Using Google Place Autocomplete API in React - javascript

I want to have an auto completing location search bar in my react component, but don't know how I would go about implementing it. The documentation says to include
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places&callback=initMap" async defer></script>
in an HTML file, and then have an initialize function pointing to an element - how would I go about doing this with my react component/JSX? I presume I would have to import the api link, but I have no clue where to go from there.
import React from 'react';
import "https://maps.googleapis.com/maps/api/js?key=MYKEY&libraries=places&callback=initMap";
const SearchBar = () => (
<input type="text" id="search"/> //where I want the google autocomplete to be
);
export default SearchBar;

Google Maps API loading via static import:
import "https://maps.googleapis.com/maps/api/js?key=MYKEY&libraries=places&callback=initMap";
is not supported, you need to consider a different options for that purpose:
reference Google Maps API JS library via /public/index.html file:
<script src="https://maps.googleapis.com/maps/api/js?key=MYKEY&libraries=places"></script>
or dynamically load JS resource, for example using this
library
Now regarding SearchBar component, the below example demonstrates how to implement a simple version of Place Autocomplete (without a dependency to Google Map instance) based on this official example
import React from "react";
/* global google */
class SearchBar extends React.Component {
constructor(props) {
super(props);
this.autocompleteInput = React.createRef();
this.autocomplete = null;
this.handlePlaceChanged = this.handlePlaceChanged.bind(this);
}
componentDidMount() {
this.autocomplete = new google.maps.places.Autocomplete(this.autocompleteInput.current,
{"types": ["geocode"]});
this.autocomplete.addListener('place_changed', this.handlePlaceChanged);
}
handlePlaceChanged(){
const place = this.autocomplete.getPlace();
this.props.onPlaceLoaded(place);
}
render() {
return (
<input ref={this.autocompleteInput} id="autocomplete" placeholder="Enter your address"
type="text"></input>
);
}
}

Here's a solution using ES6 + React Hooks:
First, create a useGoogleMapsApi hook to load the external script:
import { useEffect, useState, useCallback } from 'react'
import loadScript from 'load-script'
import each from 'lodash/each'
var googleMapsApi
var loading = false
var callbacks = []
const useGoogleMapsApi = () => {
const [, setApi] = useState()
const callback = useCallback(() => {
setApi(window.google.maps)
}, [])
useEffect(() => {
if (loading) {
callbacks.push(callback)
} else {
if (!googleMapsApi) {
loading = true
loadScript(
`https://maps.googleapis.com/maps/api/js?key=${process.env.REACT_APP_GOOGLE_MAPS_API_KEY}&libraries=places`,
{ async: true },
() => {
loading = false
googleMapsApi = window.google.maps
setApi(window.google.maps)
each(callbacks, init => init())
callbacks = []
})
}
}
}, [])
return googleMapsApi
}
export default useGoogleMapsApi
Then, here's your input component:
import React, { useRef, useEffect, forwardRef } from 'react'
import useGoogleMapsApi from './useGoogleMapsApi'
const LocationInput = forwardRef((props, ref) => {
const inputRef = useRef()
const autocompleteRef = useRef()
const googleMapsApi = useGoogleMapsApi()
useEffect(() => {
if (googleMapsApi) {
autocompleteRef.current = new googleMapsApi.places.Autocomplete(inputRef.current, { types: ['(cities)'] })
autocompleteRef.current.addListener('place_changed', () => {
const place = autocompleteRef.current.getPlace()
// Do something with the resolved place here (ie store in redux state)
})
}
}, [googleMapsApi])
const handleSubmit = (e) => {
e.preventDefault()
return false
}
return (
<form autoComplete='off' onSubmit={handleSubmit}>
<label htmlFor='location'>Google Maps Location Lookup</label>
<input
name='location'
aria-label='Search locations'
ref={inputRef}
placeholder='placeholder'
autoComplete='off'
/>
</form>
)
}
export default LocationInput
Viola!

Was making a custom address autocomplete for a sign up form and ran into some issues,
// index.html imports the google script via script tag ie: <script src="https://maps.googleapis.com/maps/api/js?key=MYKEY&libraries=places"></script>
import {useState, useRef, useEffect } from 'React'
function AutoCompleteInput(){
const [predictions, setPredictions] = useState([]);
const [input, setInput] = useState('');
const [selectedPlaceDetail, addSelectedPlaceDetail] = useState({})
const predictionsRef = useRef();
useEffect(
()=>{
try {
autocompleteService.current.getPlacePredictions({ input }, predictions => {
setPredictions(predictions);
});
} catch (err) {
// do something
}
}
}, [input])
const handleAutoCompletePlaceSelected = placeId=>{
if (window.google) {
const PlacesService = new window.google.maps.places.PlacesService(predictionsRef.current);
try {
PlacesService.getDetails(
{
placeId,
fields: ['address_components'],
},
place => addSelectedPlaceDetail(place)
);
} catch (e) {
console.error(e);
}
}
}
return (
<>
<input onChange={(e)=>setInput(e.currentTarget.value)}
<div ref={predictionsRef}
{ predictions.map(prediction => <div onClick={ ()=>handleAutoCompletePlaceSelected(suggestion.place_id)}> prediction.description </div> )
}
</div>
<>
)
}
So basically, you setup the autocomplete call, and get back the predictions results in your local state.
from there, map and show the results with a click handler that will do the follow up request to the places services with access to the getDetails method for the full address object or whatever fields you want.
you then save that response to your local state and off you go.

Related

React-Leaflet LocateControl component - keeps duplicating on each refresh

I'm using Locate Control in React Leaflet, but the Locate Control buttons are always duplicated, and sometimes I get 3 or 4 of them (see image below). I'm running the function through a useEffect with empty dependency to only fire it once, but no matter. I can target the class with display: none, but then both disappear. I feel like this might be an issue with Locate Control library? Really not sure. Open to any help or ideas.
import { useEffect } from "react"
import { useMap } from "react-leaflet"
import Locate from "leaflet.locatecontrol"
import "leaflet.locatecontrol/dist/L.Control.Locate.min.css"
const AddLocate = () => {
const map = useMap()
useEffect(() => {
const locateOptions = {
position: "bottomleft",
flyTo: true,
}
const locateControl = new Locate(locateOptions)
locateControl.addTo(map)
}, [])
return null
}
export default AddLocate;
Looks like you use a package made for leaflet. Which should for the most parts be okay. However the way you add the control is not really the react-leaflet way, where we want to add add components rather than add "stuff" directly to the map.
Below you can see how easy it is to implement a location component that you simply just can add as component within your MapContainer.
import { ActionIcon } from "#mantine/core";
import React, { useState } from "react";
import { useMapEvents } from "react-leaflet";
import { CurrentLocation } from "tabler-icons-react";
import LeafletControl from "./LeafletControl";
interface LeafletMyPositionProps {
zoom?: number;
}
const LeafletMyPosition: React.FC<LeafletMyPositionProps> = ({ zoom = 17 }) => {
const [loading, setLoading] = useState<boolean>(false);
const map = useMapEvents({
locationfound(e) {
map.flyTo(e.latlng, zoom);
setLoading(false);
},
});
return (
<LeafletControl position={"bottomright"}>
<ActionIcon
onClick={() => {
setLoading(true);
map.locate();
}}
loading={loading}
variant={"transparent"}
>
<CurrentLocation />
</ActionIcon>
</LeafletControl>
);
};
export default LeafletMyPosition;
And for LeafletControl I just have this reusable component:
import L from "leaflet";
import React, { useEffect, useRef } from "react";
const ControlClasses = {
bottomleft: "leaflet-bottom leaflet-left",
bottomright: "leaflet-bottom leaflet-right",
topleft: "leaflet-top leaflet-left",
topright: "leaflet-top leaflet-right",
};
type ControlPosition = keyof typeof ControlClasses;
export interface LeafLetControlProps {
position?: ControlPosition;
children?: React.ReactNode;
}
const LeafletControl: React.FC<LeafLetControlProps> = ({
position,
children,
}) => {
const divRef = useRef(null);
useEffect(() => {
if (divRef.current) {
L.DomEvent.disableClickPropagation(divRef.current);
L.DomEvent.disableScrollPropagation(divRef.current);
}
});
return (
<div ref={divRef} className={position && ControlClasses[position]}>
<div className={"leaflet-control"}>{children}</div>
</div>
);
};
export default LeafletControl;
I would do some debugging to that useEffect to see if it's only happening once. It's possible the entire component is mounted multiple times.

React useEffect and React-Query useQuery issue?

I'm still new to React so forgive me if this is a silly approach to this problem.
My goal: Global error handling using a context provider and a custom hook.
The Problem: I can't remove errors without them immediately being re-added.
I display my errors via this component in the shell...
import React, { useState, useEffect } from 'react'
import Alert from '#mui/material/Alert'
import Collapse from '#mui/material/Collapse'
import { useAlertContext } from '#/context/alert-context/alert-context'
export default function AppAlert () {
const [show, setShow] = useState(false)
const alertContext = useAlertContext()
const handleClose = () => {
alertContext.remove()
setShow(false)
}
useEffect(() => {
if (alertContext.alert) {
setShow(true)
}
}, [alertContext.alert])
return (
<Collapse in={show}>
<Alert severity='error' onClose={handleClose}>
{alertContext.alert}
</Alert>
</Collapse>
)
}
I have a provider setup that also exposes a custom hook...
import React, { useState, createContext, useContext } from 'react'
const AlertContext = createContext()
const AlertProvider = ({ children }) => {
const [alert, setAlert] = useState(null)
const removeAlert = () => setAlert(null)
const addAlert = (message) => setAlert(message)
return (
<AlertContext.Provider value={{
alert,
add: addAlert,
remove: removeAlert
}}
>
{children}
</AlertContext.Provider>
)
}
const useAlertContext = () => {
return useContext(AlertContext)
}
export {
AlertProvider as default,
useAlertContext
}
And finally I have a hook setup to hit an API and call throw errors if it any occur while fetching the data. I'm purposely triggering a 404 by passing a bad API path.
import { useEffect } from 'react'
import { useQuery } from 'react-query'
import ApiV4 from '#/services/api/v4/base'
import { useAlertContext } from '#/context/alert-context/alert-context'
export const useAccess = () => {
const alertContext = useAlertContext()
const route = '/accessx'
const query = useQuery(route, async () => await ApiV4.get(route), {
retry: 0
})
useEffect(() => {
if (query.isError) {
alertContext.add(query.error.toString())
}
}, [alertContext, query.isError, query.error])
return query
}
This code seems to be the issue. Because alertContext.remove() triggers useEffect here and query.error still exists, it immediately re-adds the error to the page on remove. Removing alertContext from the array works, but it is not a real fix and linter yells.
useEffect(() => {
if (query.isError) {
alertContext.add(query.error.toString())
}
}, [alertContext, query.isError, query.error])
This is a perfectly fine approach to the problem. You've also accurately identified the problem. The solution is to create a second hook with access to the methods that will modify the context. AppAlert needs access to the data in the context, and needs to update when AlertContext.alert changes. UseAccess only needs to be able to call AlertContext.add, and that method wont change and trigger a re-render. This can be done with a second Context. You can just expose one Provider and bake the actions provider into the outer context provider.
import React, { useState, createContext, useContext } from 'react'
const AlertContext = createContext()
const AlertContextActions = createContext()
const AlertProvider = ({ children }) => {
const [alert, setAlert] = useState(null)
const removeAlert = () => setAlert(null)
const addAlert = (message) => setAlert(message)
return (
<AlertContext.Provider value={{ alert }}>
<AlertContextActions.Provider value={{ addAlert, removeAlert }}>
{children}
</AlertContextActions.Provider>
</AlertContext.Provider>
)
}
const useAlertContext = () => {
return useContext(AlertContext)
}
export {
AlertProvider as default,
useAlertContext
}
Now, where you need access to the alert you use one hook and where you need access to the actions you use the other.
// in AppAlert
import { useAlertContext, useAlertContextActions } from '#/context/alert-context/alert-context'
...
const { alert } = useAlertContext()
const { removeAlert } = useAlertContextActions()
And finally
// in useAccess
import { useAlertContextActions } from '#/context/alert-context/alert-context'
...
const { addAlert } = useAlertContextActions()
So I found a solution that seems to work for my purposes. I got a hint from this article. https://mortenbarklund.com/blog/react-architecture-provider-pattern/
Note the use of useCallback above. It ensures minimal re-renders of components using this context, as the function is guaranteed to be stable (as its memoized without dependencies).
So with this I tried the following and it solved the problem.
import React, { useState, createContext, useContext, useCallback } from 'react'
const AlertContext = createContext()
const AlertProvider = ({ children }) => {
const [alert, setAlert] = useState(null)
const removeAlert = useCallback(() => setAlert(null), [])
const addAlert = useCallback((message) => setAlert(message), [])
return (
<AlertContext.Provider value={{
alert,
add: addAlert,
remove: removeAlert
}}
>
{children}
</AlertContext.Provider>
)
}
const useAlertContext = () => {
return useContext(AlertContext)
}
export {
AlertProvider as default,
useAlertContext
}
My goal: Global error handling
One problem with the above useEffect approach is that every invocation of useAccess will run their own effects. So if you have useAccess twice on the page, and it fails, you will get two alerts, so it's not really "global".
I would encourage you to look into the global callbacks on the QueryCache in react-query. They are made for this exact use-case: To globally handle errors. Note that to use context, you would need to create the queryClient inside the Application, and make it "stable" with either useRef or useState:
function App() {
const alertContext = useAlertContext()
const [queryClient] = React.useState(() => new QueryClient({
queryCache: new QueryCache({
onError: (error) =>
alertContext.add(error.toString())
}),
}))
return (
<QueryClientProvider client={queryClient}>
<RestOfMyApp />
</QueryClientProvider>
)
}
I also have some examples in my blog.

How to use Context Api in Next.js

First I would like to say to I've never worked with either next.js or the context api so please bear with me.
I'm currently working on a web application in Next.js where I have multiple pages that each contain a form. I would like to have a global state of some sort in order to be able to set and update the data from each form. All form data together
For example: page 1 = name, page 2 = description, ...
From what I've read online, I thought that using the context api would be sufficient, but I've hit a wall. When I fill in the name on the first form it doesn't get saved in the global state because it doesn't show up on the second page.
I don't understand where I went wrong so any help i more than welcome!
p.s. if i didn't explain some part right or forgot to add some code snippet please let me know.
businessContext.tsx
import { createContext, useState } from "react";
//accessible data
export interface BusinessContextData {
businessName: string;
handleBusinessName: (name: string) => void;
}
//default values
export const businessContextDefaultValue: BusinessContextData = {
businessName: "",
};
//provider
export const BusinessContext = createContext<BusinessContextData>(
businessContextDefaultValue
);
//hooks that components can use to change the values
export function useBusinessContextValue(): BusinessContextData {
const [businessName, setBName] = useState<string>("");
const handleBusinessName = (name: string) => {
setBName(name);
};
return {
businessName,
handleBusinessName,
};
}
_app.tsx
import type { AppProps } from "next/app";
import {
useBusinessContextValue,
BusinessContext,
} from "../context/businessContext";
import "../styles/global.css";
function MyApp({ Component, pageProps }: AppProps) {
const businessContextValue = useBusinessContextValue();
return (
<BusinessContext.Provider value={businessContextValue}>
<Component {...pageProps} />
</BusinessContext.Provider>
);
}
export default MyApp;
businessName.tsx - name form page (should save the given name in global state)
import { ChangeEvent, FormEvent, useContext, useState } from "react";
import { BusinessContext } from "../context/businessContext";
const IndexPage = () => {
const { handleBusinessName } = useContext(BusinessContext);
const router = useRouter();
const [businessNameState, setBusinessnameState] = useState<string>("");
const onSubmit = (e: FormEvent) => {
handleBusinessName(businessNameState);
router.push("/businessVision");
};
return (
...
<form onSubmit={(e: FormEvent) => onSubmit(e)}>
<div className="formInputRow">
<input
className="formInput"
type="text"
placeholder="Business name"
required
value={businessNameState}
onChange={(val: ChangeEvent<HTMLInputElement>) =>
setBusinessnameState(val.target.value)
}
/>
</div>
<button type="submit">
Next
</button>
</form>
...
);
};
export default IndexPage;
businessVision.tsx - should display business name from global state
import { ChangeEvent, FormEvent, useContext, useEffect, useState } from "react";
import { BusinessContext } from "../context/businessContext";
const BusinessVisionpage = () => {
const { businessName } = useContext(BusinessContext);
const router = useRouter();
const [businessVisionState, setBusinessVisionState] = useState<string>("");
return (
...
<h1>
<span>{businessName}</span>
</h1>
...
);
};
Your context is setup correctly, but you'll need to prevent the <form>'s submit default behaviour so that the navigation to the next page can happen properly.
const onSubmit = (e: FormEvent) => {
e.preventDefault(); // Prevents submit default behaviour
handleBusinessName(businessNameState);
router.push("/businessVision");
};

REST API Response Flickering on Page Load

I've got component that displays contact information from a dealer as chosen by the user. To be more specific, a user selects their location, setting a cookie which then is used to define the API call. I pull in the contact information of the dealer in that location using Axios, store it in a context, and then display the information as necessary through several components: the header, a "current location" component etc. However, I'm having an issue with the content flickering each time the page is refreshed.
I've tried storing the JSON response in local storage, but, for a brief moment on page load, it shows as undefined, making the flicker continue. Obviously, I'm needing to eliminate that so that the data persists.
I've got it working via ApiContext, and I'm displaying the data in my Header component. Below is the code for both:
ApiContext.tsx
import React, { createContext, useEffect, useState } from 'react';
import axios from 'axios';
const contextObject = {} as any;
export const context = createContext(contextObject);
export const ApiContext = ({ children }: any) => {
const [selectedDealer, setselectedDealer] = useState(`1`);
useEffect(() => {
axios
.get(`${process.env.GATSBY_API_ENDPOINT}/${selectedDealer}`)
.then((response) => setselectedDealer(response.data));
}, [selectedDealer]);
const changeDealer = (id: any) => {
setselectedDealer(id);
};
const { Provider } = context;
return (
<Provider value={{ data: selectedDealer, changeDealer: changeDealer }}>
{children}
</Provider>
);
};
Header.tsx
import React, { ReactNode, useContext, useEffect, useState } from 'react';
import Logo from 'assets/svg/logo.svg';
import css from 'classnames';
import { Button } from 'components/button/Button';
import { Link } from 'components/link/Link';
import { MenuIcon } from 'components/menu-icon/MenuIcon';
import { context } from 'contexts/ApiContext';
import { NotificationBar } from '../notification-bar/NotificationBar';
import s from './Header.scss';
import { MainNav } from './navigation/MainNav';
interface HeaderProps {
navigationContent: ReactNode;
}
export const Header = ({ navigationContent }: HeaderProps) => {
const [scrolled, setScrolled] = useState(false);
const [open, setOpen] = useState(false);
const data = useContext(context);
const buttonLabel = data ? data.name : 'Find a Dealer';
const buttonLink = data ? `tel:${data.phone}` : '/find-a-dealer';
useEffect(() => {
const handleScroll = () => {
const isScrolled = window.scrollY > 10;
if (isScrolled !== scrolled) {
setScrolled(!scrolled);
}
};
document.addEventListener('scroll', handleScroll, { passive: true });
return () => {
document.removeEventListener('scroll', handleScroll);
};
}, [scrolled]);
return (
<>
<NotificationBar notificationContent={navigationContent} />
<header className={scrolled ? css(s.header, s.header__scrolled) : s.header}>
<nav className={s.header__navigation}>
<ul className={s.header__container}>
<li className={s.header__logo}>
<Link to="/" className={s.header__link}>
<Logo />
</Link>
</li>
<li className={s.header__primary}>
<MainNav navigationItems={navigationContent} />
</li>
<li className={s.header__utility}>
<Button href={buttonLink}>{buttonLabel}</Button>
</li>
<li className={s.header__icon}>
<MenuIcon onClick={() => setOpen(!open)} />
</li>
</ul>
</nav>
</header>
</>
);
};
I would assume that this is because the API call is being triggered each time the page is refreshed, so I wonder if there's any way to persist the data in a more efficient way?
Thanks in advance!
Your ApiContext.tsxcould persist the data in localStorage is such a way:
import React, { createContext } from 'react';
import axios from 'axios';
import { makeUseAxios } from 'axios-hooks';
import { useCookie } from 'hooks/use-cookie';
const contextObject = {} as any;
export const context = createContext(contextObject);
const useAxios = makeUseAxios({
axios: axios.create({ baseURL: process.env.GATSBY_API_ENDPOINT }),
});
const loadData = (cookie) => {
const stored = localStorage.getItem("data");
const parsed = JSON.parse(stored);
// You can also store a lastSync timestamp along with the data, so that you can refresh them if necessary
if (parsed) return parsed;
const [{data}] = useAxios(`${cookie}`);
if (!isEqual(parsed, data)) {
localStorage.setItem('data', JSON.stringify(data));
}
return data
}
export const ApiContext = ({ children }: any) => {
const [cookie] = useCookie('one-day-location', '1');
const [{ data }] = loadData(cookie);
const { Provider } = context;
return <Provider value={data}>{children}</Provider>;
};
The above implementation will only fetch the data once, so remember to refresh them at some point inside your code and update the localStorage item, or use a timestamp to compare and force the api call as commented in my code.
Keep in mind that even this implementation may take a fraction of a second to be completed, so I would suggest to always use loaders/spinners/skeletons while your application is fetching the required data.
I got this worked out, using a hook that persists my state, storing it in a localStorage item.
usePersistState.ts
import { useEffect, useState } from 'react';
export const usePersistState = (key: string, defaultValue: string) => {
const [value, setValue] = useState(() => {
if (typeof window !== 'undefined') {
const stickyValue = window.localStorage.getItem(key);
return stickyValue !== null ? JSON.parse(stickyValue) : defaultValue;
}
});
useEffect(() => {
window.localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue];
};
Then, in ApiContext, I set my default state, but when that state changes, it updates and persists the state. Here's my context component now:
ApiContext.tsx
import React, { createContext, useEffect } from 'react';
import { usePersistState } from 'hooks/use-persist-state';
import axios from 'axios';
const contextObject = {} as any;
export const context = createContext(contextObject);
const LOCAL_STORAGE_KEY_SELECTED_DEALER = 'selectedDealerInformation';
export const ApiContext = ({ children }: any) => {
const [selectedDealer, setselectedDealer] = usePersistState(LOCAL_STORAGE_KEY_SELECTED_DEALER, '1');
useEffect(() => {
axios
.get(`${process.env.GATSBY_API_ENDPOINT}/${selectedDealer}`)
.then((response) => setselectedDealer(response.data));
}, [selectedDealer]);
const changeDealer = (id: any) => {
setselectedDealer(id);
};
localStorage.setItem(LOCAL_STORAGE_KEY_SELECTED_DEALER, JSON.stringify(selectedDealer));
const { Provider } = context;
return (
<Provider value={{ data: selectedDealer, changeDealer: changeDealer }}>{children}</Provider>
);
};

text box value not passing to the api call

I followed the below link and tried to do a poc https://www.reddit.com/r/reactjs/comments/82mxz3/how_to_make_an_api_call_on_props_change
In Searchbar I created a textbox, and I am getting the value but in index.js when I give componentWillRecieveProps and print the values to pass it to the api but nothing printing. Can you tell me how to fix it.Providing updated code sandbox and code snippet below.
https://codesandbox.io/s/eloquent-galielo-14874
//import React from "react";
import ReactDOM from "react-dom";
import React, { Fragment, useState, Component } from "react";
import "./styles.css";
class SearchBar extends Component {
state = {
groupCheckBoxValues: [],
groupRadioValue: "PRO"
};
getInitialState() {
return { value: "Hello!" };
}
handleChange(event) {
console.log("handleChange", event.target.value);
this.setState({ value: event.target.value });
}
componentWillReceiveProps({ search }) {
console.log(search);
}
componentDidMount() {
this.fetchdata("story");
}
fetchdata(type = "", search_tag = "") {
var url = "https://hn.algolia.com/api/v1/search?tags=";
fetch(`${url}${type}&query=${search_tag}`)
.then(res => res.json())
.then(data => {
this.props.getData(data.hits);
});
}
render() {
return (
<input
type="text"
value={this.state.value}
onChange={this.handleChange}
/>
);
}
}
export default SearchBar;
You're not actually passing the text input's value to the fetch request.
I recommend something like this:
class SearchBar extends Component {
searchByKeyword = ({target}) => {
await this.getQuery("story", target.value)
}
async componentDidMount() {
await this.getQuery("story", "butts");
}
getQuery = async(type = "", search_tag = "") => {
var url = "https://hn.algolia.com/api/v1/search?tags=";
const resp = await fetch(`${url}${type}&query=${search_tag}`)
return resp.json()
}
render() {
return (
<input
type="text"
onChange={this.searchByKeyword}
/>
);
}
}
I removed the state and things because it doesn't seem entirely germane to the question.
There're some mistakes in your demo.
The function setState function won't work cause you didn't bind it to your component.
The UNSAFE_componentWillReceiveProps() is unsafe function. It will be deprecated soon.
And you shouldn't use { useState } hook in Class Component.
I think you should check some tutorials for fetching data from reactjs site. It will provide some good practices for you :)

Categories

Resources