React google location doesnt work with Hooks - reactjs

I was trying the react google api in my project. I created the count in Google Cloud and i have the api.
I have this code:
import { GoogleComponent } from "react-google-location";
export default function Home() {
const API_KEY_GOOGLE = "XXXXX";
return (
<div className="home">
<h1>GHome</h1>
<div>
<GoogleComponent
apiKey={API_KEY_GOOGLE}
language={"en"}
country={"country:in|country:us"}
coordinates={true}
// locationBoxStyle={"custom-style"}
// locationListStyle={"custom-style-list"}
onChange={e => {
setPlace({ place: e });
}}
/>
</div>
</div>
);
}
And appears that in the console:

If the application does not contain any other part where you have for example <ul> and <li> combinations or generating other HTML elements with map() without added key attribute for example then the issue is most probably with passed country attribute.
Based on the documentation of react-google-location:
Use multiple languages
Pass key and value pair in the country object like this:
country={in|country:pr|country:vi|country:gu|country:mp}
Your country attribute has {"country:in|country:us"}. Maybe it is worth to test as the following:
<GoogleComponent
apiKey={API_KEY_GOOGLE}
language={"en"}
country={"in|country:us"}
coordinates={true}
onChange={e => {
setPlace({ place: e });
}} />
Or my second guess:
<GoogleComponent
apiKey={API_KEY_GOOGLE}
language={"en"}
country={"in|country:in|country:us"}
coordinates={true}
onChange={e => {
setPlace({ place: e });
}} />
I hope that helps!

Related

How to link a text input with associated error message in a reusable React component

I have a React component called TextInput which looks like this:
const TextInput = React.forwardRef<HTMLInputElement, ITextInputProps>(
({ error, ...props }, ref) => {
return (
<>
<input
type="text"
aria-describedby="" // ๐Ÿ‘ˆ see here
ref={ref}
{...props}
/>
{error && (
<p> // ๐Ÿ‘ˆ how can I reference this?
{error}
</p>
)}
</>
)}
)
For accessibility, I am trying to link the error message to the field using aria-describedby.
In a normal HTML document I would just use the id of the paragraph tag, but with reusability in mind, I would like to learn how to do this in a React-like way.
The use of refs comes to mind, but I'm not sure how to apply it in this scenario.
I would say that the best way to do this is to create a required prop that handles the ID and you apply that ID on the aria fields.
With that in mind, if what you wanted is to generate the data dinamically you could use a useEffect running it only once and setting a state inside it.
function Component() {
const [describedBy, setDescribedBy] = useState()
useEffect(() => {
setDescribedBy(generateRandomString());
}, [])
return (
<div aria-describedby={describedBy}></div>
)
}

How can I dynamically tie my form into Formik, in React functional component

I'm building a React component which loads form data dynamically from an external API call. I'll need to submit it back to another API endpoint after the user completes it.
here it is:
import React, { useEffect, useState } from "react";
import axios from "axios";
import TextField from "#material-ui/core/TextField";
import { useFormik } from "formik";
const FORM = () => {
const [form, setForm] = useState([]);
const [submission, setSubmission] = useState({});
const formik = useFormik({
initialValues: {
email: "",
},
});
useEffect(() => {
(async () => {
const formData = await axios.get("https://apicall.com/fakeendpoint");
setForm(formData.data);
})();
}, []);
return (
<form>
{form.map((value, index) => {
if (value.fieldType === "text") {
return (
<TextField
key={index}
id={value.name}
label={value.label}
/>
);
}
if (value.fieldType === "select") {
return (
<TextField
select={true}
key={index}
id={value.name}
label={value.label}
>
{value.options.map((option) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</TextField>
);
}
})}
<button type="submit">Submit</button>
</form>
);
};
export default FORM;
The API call is coming in ok (yeah i now i need some error handle on that) and I am able to populate the fields and get the form on the page. Where I am having trouble (and im newer to Formik so bear with me) is i need to do validation and submission. I do not really know how to handle the values, usually i'd write some type of static code for the variables, test them and then submit.
usually i'd set a field for "name" and one for "email" for example. In this case i can't write those fields in because they come from the API and we have no idea what they are until we get the call response.
My code handles the field creation but i need to wire for validation and submission and want to use Formik.
How can i accomplish a dynamic form (thru formik) which is wired for validation and submission?
I had the same problem, and I solved it with <Formik> component instead of useFormik() hook. not sure why useFormik fails on dynamic validation, but anyway, after switching to <Formik>, below is the code which worked for me. please check key points after the below code snippet as well.
<Formik
innerRef={formikRef}
initialValues={request || emptyRequest //Mostafa: edit or new: if request state is avaialble then use it otherwise use emptyRequest.
}
enableReinitialize="true"
onSubmit={(values) => {
saveRequest(values);
}}
validate={(data) => {
let errors = {};
if (!data.categoryId) {
errors.categoryId = 'Request Category is required.';
}
//Mostafa: Dynamic validation => as the component is re-rendered, the validation is set again after request category is changed. React is interesting and a lot of repetitive work is done.
if (requestCategoryFields)
requestCategoryFields.map(rcField => {
if (rcField.fieldMandatory) { //1- check Mandatory
if (!data.dynamicAttrsMap[rcField.fieldPropertyName]) {
if (!errors.dynamicAttrsMap) //Mostafa: Need to initialize the object in errors, otherwise Formik will not work properly.
errors.dynamicAttrsMap = {}
errors.dynamicAttrsMap[rcField.fieldPropertyName] = rcField.fieldLabel + ' is required.';
}
}
if (rcField.validationRegex) { //2- check Regex
if (data.dynamicAttrsMap[rcField.fieldPropertyName]) {
var regex = new RegExp(rcField.validationRegex); //Using RegExp object for dynamic regex patterns.
if (!regex.test(data.dynamicAttrsMap[rcField.fieldPropertyName])) {
if (!errors.dynamicAttrsMap) //Mostafa: Need to initialize the object in errors, otherwise Formik will not work properly.
errors.dynamicAttrsMap = {}
if (errors.dynamicAttrsMap[rcField.fieldPropertyName]) //add an Line Feed if another errors line already exists, just for a readable multi-line message.
errors.dynamicAttrsMap[rcField.fieldPropertyName] += '\n';
errors.dynamicAttrsMap[rcField.fieldPropertyName] = rcField.validationFailedMessage; //Regex validaiton error and help is supposed to be already provided in Field defintion by admin user.
}
}
}
});
return errors;
}}>
{({errors,handleBlur,handleChange,handleSubmit,resetForm,setFieldValue,isSubmitting,isValid,dirty,touched,values
}) => (
<form autoComplete="off" noValidate onSubmit={handleSubmit} className="card">
<div className="grid p-fluid mt-2">
<div className="field col-12 lg:col-6 md:col-6">
<span className="p-float-label p-input-icon-right">
<Dropdown name="categoryId" id="categoryId" value={values.categoryId} onChange={e => { handleRequestCategoryChange(e, handleChange, setFieldValue) }}
filter showClear filterBy="name"
className={classNames({ 'p-invalid': isFormFieldValid(touched, errors, 'categoryId') })}
itemTemplate={requestCategoryItemTemplate} valueTemplate={selectedRequestCategoryValueTemplate}
options={requestCategories} optionLabel="name" optionValue="id" />
<label htmlFor="categoryId" className={classNames({ 'p-error': isFormFieldValid(touched, errors, 'categoryId') })}>Request Category*</label>
</span>
{getFormErrorMessage(touched, errors, 'siteId')}
</div>
{
//Here goes the dynamic fields
requestCategoryFields && requestCategoryFields.map(rcField => {
return (
<DynamicField field={rcField} values={values} touched={touched} errors={errors} handleBlur={handleBlur}
handleChange={handleChange} isFormFieldValid={isFormFieldValid} getFormErrorMessage={getFormErrorMessage}
crudService={qaCrudService} toast={toast} />
)
})
}
</div>
<div className="p-dialog-footer">
<Button type="button" label="Cancel" icon="pi pi-times" className="p-button p-component p-button-text"
onClick={() => {
resetForm();
hideDialog();
}} />
<Button type="submit" label="Save" icon="pi pi-check" className="p-button p-component p-button-text" />
</div>
</form>
)}
Key points:
My dynamic fields also come from a remote API, by selecting the categoryId DropDown, and are set into the requestCategoryFields state.
I store the dynamic fields' values in a nested object called dynamicAttrsMap inside my main object which is called request as I have static fields as well.
I used validate props which includes one static validation for categoryId field, and then a loop (map) that adds dynamic validation for all other dynamic fields. this strategy works as with every state change, your component is re-rendered and your validation is created from scratch.
I have two kinds of validation, one checks the existence of value for mandatory fields. And the other checks a regex validation for the contents of the dynamic fields if needed.
I moved the rendering of dynamic fields to a separate component <DynamicField> for better modularization. please check my DynamicField component
you can move the validation props to a const for better coding; here my code is a bit messy.
I used two const, for better coding, for error messages and CSS as below:
const isFormFieldValid = (touched, errors, name) => { return Boolean(getIn(touched, name) && getIn(errors, name)) };
const getFormErrorMessage = (touched, errors, name) => {
return isFormFieldValid(touched, errors, name) && <small className="p-error">{getIn(errors, name)}</small>;
};
please check out my complete component at my GitHub Here for better understanding. and comment for any clarification if you need. because I know how tricky Formik can sometimes be. we have to help out each other.
Adding enableReinitialize to formik
useFormik({
initialValues: initialData,
enableReinitialize: true,
onSubmit: values => {
// Do something
}
});
FORMIK Doc

How to add places parameter in google map API URL to fetch specific type of location for auto-complete

I'm trying to implement a autocomplete search input bar for auto-completing the results of searching colleges and university in my React app.
my code is as per the following:
import React, { Component } from 'react'
import './App.css'
import PlacesAutocomplete from "react-places-autocomplete";
export class App extends Component {
constructor(props) {
super(props);
this.state = { address: '' };
}
handleChange = address => {
this.setState({ address });
};
render() {
return (
<div>
<PlacesAutocomplete
value={this.state.address}
onChange={this.handleChange}
onSelect={this.handleSelect}
// searchOptions={searchOptions}
shouldFetchSuggestions={this.state.address.length > 3}
>
{({ getInputProps, suggestions, getSuggestionItemProps, loading }) => (
<div >
<input maxLength="50" className="typo"{...getInputProps({ placeholder: "Search Your College" })} />
<div>
{loading ? <div>...loading</div> : null}
{suggestions.map(suggestion => {
const style = {
backgroundColor: suggestion.active ? "#41b6e6" : "",
};
return (
<div className="suggestion" {...getSuggestionItemProps(suggestion, { style })}>
{suggestion.description}
</div>
);
})}
</div>
</div>
)}
</PlacesAutocomplete>
</div>
)
}
}
export default App
and obviously the <script> tag in index.html
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places"></script>
According to The gmaps docs, in order to only get selective results from these types: school,university,secondary-school and college I need to modify it. But, i clearly didn't got the docs correctly and wasn't able to understand it well.
so I wanted to know whats the correct way of formatting the url?
=> React-Places-Autocomplete docs
Note that what you're using is Place Autocomplete, not Place Search. The supported types for Autocomplete are listed in table 3:
geocode
address
establishment
(regions)
(cities)
So using other types like school won't work with Autocomplete; only with the Place Search service.
Hope this helps!

How can I on Submit my Form redirect the Results to another Page in React

I am trying to get the search results to display on another page in React Js. For now, when I submit my search form, it will display the results on the same page which I don't want. I have been trying to do this for the past 4 hours (I am new to ReactJS) and I have been seeing a lot of suggestions online but nothing seems to work as I was hoping.
I have even tried to use react redux, react router and much more example online but does not work, don't what I'm doing wrong.
How can I approach it? I would please like someone to probably put me on the right track in regards to the code I provide below and also your suggestions/feedback are welcome.
Thank you in advance.
Here is my search engine component responsible for the form
const SearchEngine = (props) => {
return (
<div>
<h3 className="spacer">Search Engine</h3>
<form onSubmit={props.getTrend}>
<div class="input-group md-form form-sm form-2 pl-0">
<input class="form-control my-0 py-1 red-border" type="text" name="keyword" placeholder="Enter your keyword" aria-label="Search" />
<div class="input-group-append">
<button class="input-group-text red lighten-3" id="basic-text1"><i class="fa fa-search text-grey" aria-hidden="true"></i></button>
</div>
</div>
</form>
</div>
);
}
export default SearchEngine;
and here is the result component where I would like to display the results
const Results = (props) => (
<div>
Hello
</div>
);
export default Results;
After receiving your api calls you can throw a push and move to another page. For yours it may look something like this.
getTrend = async (e) => {
e.preventDefault();
const keyword = e.target.elements.keyword.value;
const api_call = await fetch(`http://localhost:8081/trend/twitter?keyword=${keyword}`); //make API call
const data = await api_call.json();
if (keyword) {
this.setState({
tweets: data
});
console.log(this.state.tweets);
this.props.history.push({
pathname: '/results',
state: { detail: data }
})
}
else {
this.setState({
tweets: undefined,
error: "Please enter the values"
});
}
}
Then, in your App.js you can access it with
props.location.state.detail
This may require that you use withRouter as a HOC. One thing I would change is grab your api in componentDidMount. However, it would be much easier if you went about this with hooks. While it may require some refractoring, you could make your api call with a hook, which is a ton simpler to get up and running.

How to initialize a StandaloneSearchBox component with a given address?

I have a react component for a google.maps.places.SearchBox without the map, which is a StandaloneSearchBox. I want to pass it props with the initial value (which is only the formated version of the address, e.g "London, Kentucky, ร‰tats-Unis") and then be able to change the address in the input field.
I have a places property in the state, which I want to hold the place object. How can I pass in the beginning in the componentDidMount() method the initial value so I can set it to the places object? It doesn't work in this way.
const PlacesWithStandaloneSearchBox = compose(
withProps({
googleMapURL: googleMapsURI,
loadingElement: <div style={{ height: `100%` }} />,
containerElement: <div style={{ height: `400px` }} />
}),
lifecycle({
componentWillMount() {
console.log("componentWillMount");
const refs = {};
this.setState({
places: [],
onSearchBoxMounted: ref => {
refs.searchBox = ref;
},
onPlacesChanged: () => {
const places = refs.searchBox.getPlaces();
this.setState({
places
});
}
})
},
componentDidMount() {
this.setState({
places: this.props.value
});
}
}),
withScriptjs
)(props =>
<div className="fretlink-input form-group" data-standalone-searchbox="">
<StandaloneSearchBox
ref={ props.onSearchBoxMounted }
bounds={ props.bounds }
onPlacesChanged={ props.onPlacesChanged }>
<input
className="form-control"
placeholder={ props.placeholder }
type="text" />
</StandaloneSearchBox>
<ol>
{ props.places.map(({ place_id, formatted_address, geometry: { location } }) =>
<li key={ place_id }>
{ formatted_address }
{" at "}
({location.lat()}, {location.lng()})
</li>
)}
</ol>
</div>
);
export default PlacesWithStandaloneSearchBox;
It appears that initializing the StandaloneSearchBox with an actual Google Maps place object, based on a search on address text or lat lng, isn't possible out of the box with a component prop or otherwise.
I think the best you can do is implement this yourself, by manually doing a search with the Google Maps places API with whatever data you have, getting that initial place, and setting it via this.setState in componentDidMount as in your example.
However, I didn't do this, because I found a solution to this that's a lot simpler, and I think will also apply in your case.
If you have the formatted address, there may actually be no reason to perform the search and populate the component with a full Google Maps place at all. All you really need is that text showing in the input. When you search for a new address, sure, you need all the data (lat lng etc) -- but that is already working. For the initial address, you probably already have this data. It's really just what you show in the <input> that's your concern.
Fortunately, it's really easy to separate the behaviour of the <input> from the behaviour of the StandaloneSearchBox because the <input> is just a child component that you can fully control.
What I did was just to use some more component state to hold the text that the input should show, and then make it a controlled component. Everything else works in exactly the same way, it's just that I take control of the 'view' which is the text displayed in the <input>.
Here is a rough example of what I mean, using your code:
// init the state with your initial value "1 Something Street, etc"
const [text, setText] = useState(INITIAL_VALUE_HERE)
<StandaloneSearchBox
ref={ props.onSearchBoxMounted }
bounds={ props.bounds }
// have onPlacesChanged set the state when a new option is picked
// to whatever the address text of the selected place is
// (needs to be implemented appropriately in onPlacesChanged of course)
onPlacesChanged={() => { props.onPlacesChanged(setText) }}
>
<input
className="form-control"
placeholder={ props.placeholder }
type="text"
value={text} // control the input value with state
onChange={(e) => { setText(e.target.value) }} // update the state when you type something
/>
</StandaloneSearchBox>
You can check my answer here: Initialize React Google Maps StandaloneSearchBox with geocode
In general you can't use address as a filter for SearchBox results, but you can specify the area in which to search for places:
<StandaloneSearchBox
ref={props.onSearchBoxMounted}
bounds={props.bounds}
onPlacesChanged={props.onPlacesChanged}
defaultBounds={new google.maps.LatLngBounds(
new google.maps.LatLng(40.712216, -74.22655),
new google.maps.LatLng(40.773941, -74.12544)
)}

Resources