I am trying to set a specific country restriction using react-google-maps StandaloneSearchBox.
I have tried componentRestrictions, but I'm not sure how to use it.
Sharing my code below:
export const AutoCompleteSearchBox = compose(
withProps({
googleMapURL:googleMapUrl,
loadingElement: <div style={{ height: `100%` }} />,
containerElement: <div style={{ height: `400px`, top:'3px' }} />,
}),
lifecycle({
componentWillMount() {
const refs = {}
this.setState({
types: ['(regions)'],
componentRestrictions: {country: "bd"},
onSearchBoxMounted:ref =>{ refs.searchBox = ref; },
onPlacesChanged:()=>{
const places = refs.searchBox.getPlaces();
this.props.onPlacesChanged(places);
},
})
const options = {
types: ['(regions)'],
componentRestrictions:{ country: 'bd' }
}
},
}),
withScriptjs
)`(props =>
<div data-standalone-searchbox="">
<StandaloneSearchBox
ref={props.onSearchBoxMounted}
bounds={props.bounds}
onPlacesChanged={props.onPlacesChanged}
controlPosition={ window.google.maps.ControlPosition.TOP_LEFT}
>
<TextField
className={props.inputClass}
placeholder={props.inputPlaceholder}
label={props.inputLabel}
name={props.inputName}
value={props.inputValue}
onChange={props.inputOnChange}
helperText={props.inputHelperText}
error={props.inputError}
/>
</StandaloneSearchBox>
</div>
);`
How can I solve this problem?
You can't add such restrictions for the SearchBox results, but you can specify the area towards which to bias query predictions. Predictions are biased towards, but not restricted to, queries targeting these bounds.
If you want to show only specific places, then you can Google Place Autocomplete feature. For it you don't event need to use additional React libraries for Google Maps. Here's the example:
import React, { Component } from 'react';
import Script from 'react-load-script'
class LocationMap extends Component {
handleScriptLoad() {
const inputEl = document.getElementById('address-input');
/*global google*/
var options = {
//types: ['address'],
componentRestrictions: {country: 'by'}
};
this.autocomplete = new google.maps.places.Autocomplete(inputEl, options);
this.autocomplete.addListener('place_changed', this.handlePlaceSelect.bind(this));
}
handlePlaceSelect() {
console.log(this.autocomplete.getPlace());
}
render() {
return (
<section>
<Script
url="https://maps.googleapis.com/maps/api/js?key=API_KEY&v=3.33&libraries=places&language=en®ion=US"
onLoad={this.handleScriptLoad.bind(this)}
/>
<div className="form-group">
<label htmlFor="address-map">Enter address</label>
<input type="text"
autoComplete="new-password"
className="form-control"
id="address-input"
name="address"/>
</div>
</section>
);
}
}
export default LocationMap;
Don't forget to add react-load-script package: npm i react-load-script --save
Related
JS 13 and inside my ReadMoreButton client component i push my article data using useRouter hook of NEXT.
Not i can not use useRouter hook inside NEXT.JS server component so here i fetch searchParams and fetch that data.
here problem is before rendering i am checking if searchParams are defined or not not if i check in development everything work fine it render data but in production mode it show page not found error even if data is correctly send.
when i run next build it give me following output Output
and i am running side in production mode using next start and it show page not found when i do /article?serchParamsData.
You can check my whole code here : https://github.com/ssiwach8888/Next.JS-News-App
i also deploy production build on Vercel but it also show same error.
I am using NEXT.JS 13 with typescript
# ReadMoreButton.tsx "First Control goes here."
"use client";
type Props = {
article: NewsData;
};
import { useRouter } from "next/navigation";
//For navigate to SSC
const ReadMoreButton = ({ article }: Props) => {
const router = useRouter();
const handleClick = () => {
const queryString = Object.entries(article)
.map(([key, value]) => `${key}=${value}`)
.join("&");
const url = `/article?${queryString}`;
router.push(url);
};
return (
<button
className="bg-orange-400 h-10 rounded-b-lg dark:text-gray-900 hover:bg-orange-500"
onClick={handleClick}
>
Read More
</button>
);
};
export default ReadMoreButton;
# Article.tsx "Then we navigate to this page."
type Props = {
searchParams?: NewsData;
};
import { notFound } from "next/navigation";
import LiveTimestamp from "../Components/LiveTimestamp";
import Link from "next/link";
const ArticlePage = ({ searchParams }: Props) => {
if (
(searchParams && Object.entries(searchParams).length === 0) ||
!searchParams
) {
return notFound();
}
const article: NewsData = searchParams;
return (
<article className="mt-6">
<section className="flex flex-col lg:flex-row pb-24 px-0 lg:px-10">
<img
src={article.image === "null" ? "/no-image.jpeg" : article.image}
alt={article.title}
className="h-50 max-w-md mx-auto md:max-w-lg lg:max-w-xl object-contain rounded-lg shadow-md"
/>
<div className="px-8">
<Link legacyBehavior href={article.url || ""}>
<a target="_blank">
<h1 className="headerTitle hover:underline cursor-pointer px-0 pb-2">
{article.title}
</h1>
</a>
</Link>
<div className="flex divide-x-2 space-x-4">
<h2 className="font-bold">
By: {article.author !== "null" ? article.author : "Unknown"}
</h2>
<h2 className="font-bold pl-4">Source: {article.source}</h2>
<p className="pl-4">
<LiveTimestamp
time={
article.published_at === "null" ? "" : article.published_at
}
/>
</p>
</div>
<p className="pt-4 text-lg">{article.description}</p>
</div>
</section>
</article>
);
};
export default ArticlePage;
You just need to put the article page in [bracket] to make it dynamic so next js can fetch all pages otherwise it would display blank----
change article folder to [article]
more reference https://nextjs.org/docs/routing/dynamic-routes
I have a project with Parcel and Preact (to implement Algolia Autocomplete Search) and I suddenly got an error while npx parcel build theme/app-home.tsx --log-level verbose.
It worked before and I'm working with git but I can't found what changed to break the build.
The error:
Building app-home.tsx...
thread '<unnamed>' panicked at 'cannot access a scoped thread local variable without calling `set` first', /Users/runner/.cargo/registry/src/github.com-1ecc6299db9ec823/scoped-tls-1.0.0/src/lib.rs:168:9
🚨 Build failed.
#parcel/transformer-js: cannot access a scoped thread local variable without calling `set` first
Error: cannot access a scoped thread local variable without calling `set` first
at Object.transform (/Users/cozarkd/Documents/GitHub/projectX/node_modules/#parcel/transformer-js/lib/JSTransformer.js:365:31)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async Transformation.runTransformer (/Users/cozarkd/Documents/GitHub/projectX/node_modules/#parcel/core/lib/Transformation.js:617:5)
at async Transformation.runPipeline (/Users/cozarkd/Documents/GitHub/projectX/node_modules/#parcel/core/lib/Transformation.js:366:36)
at async Transformation.runPipelines (/Users/cozarkd/Documents/GitHub/projectX/node_modules/#parcel/core/lib/Transformation.js:244:40)
at async Transformation.run (/Users/cozarkd/Documents/GitHub/projectX/node_modules/#parcel/core/lib/Transformation.js:170:19)
at async Child.handleRequest (/Users/cozarkd/Documents/GitHub/projectX/node_modules/#parcel/workers/lib/child.js:217:9)
The tsx file:
/** #jsx h */
import {
autocomplete,
AutocompleteComponents,
getAlgoliaResults,
} from '#algolia/autocomplete-js';
import algoliasearch from 'algoliasearch';
import { h, Fragment } from 'preact';
const appId = 'theappid';
const apiKey = 'theapikey';
const searchClient = algoliasearch(appId, apiKey);
/* const querySuggestionsPlugin = createQuerySuggestionsPlugin({
searchClient,
indexName: 'database_query_suggestions',
getSearchParams() {
return {
hitsPerPage: 5,
};
},
}); */
autocomplete({
// debug: true,
container: '#autocomplete',
placeholder: 'Escribe aquÃ, sin miedo',
openOnFocus: false,
defaultActiveItemId: 0,
autoFocus: true,
getSources({ query }) {
return [
{
sourceId: 'plantag',
getItemUrl({ item }) {
return item.url;
},
getItems() {
return getAlgoliaResults({
searchClient,
queries: [
{
indexName: 'database',
query,
params: {
hitsPerPage: 10,
clickAnalytics: true,
attributesToSnippet: [
'name:10',
'nombre'
],
snippetEllipsisText: '…',
},
},
],
});
},
templates: {
item({ item, components }) {
return <ProductItem hit={item} components={components} />;
},
noResults() {
return 'No hay plantas coincidentes :(';
},
},
},
];
},
// Default Navigator API implementation
navigator: {
navigate({ itemUrl }) {
window.location.assign(itemUrl);
},
navigateNewTab({ itemUrl }) {
const windowReference = window.open(itemUrl, '_blank', 'noopener');
if (windowReference) {
windowReference.focus();
}
},
navigateNewWindow({ itemUrl }) {
window.open(itemUrl, '_blank', 'noopener');
},
},
});
function ProductItem({ hit, components }: ProductItemProps) {
return (
<div className="c-single-result">
<a href={hit.url} className="aa-ItemLink">
<div className="l-flex-container">
<div className="aa-ItemContent">
<div className="aa-ItemContentBody">
<div className="aa-ItemContentTitle">
<components.Snippet hit={hit} attribute="nombre" />
</div>
</div>
<div className="aa-ItemContentSubtitle">
<components.Snippet hit={hit} attribute="name" />
</div>
<div className="aa-ItemActions">
<button
className="aa-ItemActionButton aa-DesktopOnly aa-ActiveOnly"
type="button"
title="Select"
style={{ pointerEvents: 'none' }}
>
<svg viewBox="0 0 30 27" width="30" height="27" fill="currentColor">
<path d="M10.0611 23.8881C10.6469 24.4606 10.6469 25.389 10.0611 25.9615C9.47533 26.5341 8.52558 26.5341 7.9398 25.9615L0.441103 18.632C0.4374 18.6284 0.433715 18.6248 0.430051 18.6211C0.164338 18.3566 0.000457764 17.994 0.000457764 17.594C0.000457764 17.3952 0.0409356 17.2056 0.114276 17.0328C0.187475 16.8598 0.295983 16.6978 0.439798 16.5572L7.9398 9.22642C8.52558 8.65385 9.47533 8.65385 10.0611 9.22642C10.6469 9.79899 10.6469 10.7273 10.0611 11.2999L5.12178 16.1278H13.5005C20.9565 16.1278 27.0005 10.2202 27.0005 2.93233V1.46616C27.0005 0.656424 27.672 -1.90735e-06 28.5005 -1.90735e-06C29.3289 -1.90735e-06 30.0005 0.656424 30.0005 1.46616V2.93233C30.0005 11.8397 22.6134 19.0602 13.5005 19.0602H5.12178L10.0611 23.8881Z"/>
</svg>
</button>
</div>
</div>
<div className="aa-ItemIcon aa-ItemIcon--picture aa-ItemIcon--alignTop">
<img src={hit.image} alt={hit.name} width="40" height="40" />
</div>
</div>
</a>
</div>
);
}
I can provide more info if needed. Did some search but I couldn't find anything related to Parcel with that error.
I'm able to repro the problem, and it looks like this might be a bug in parcel related to the JSX pragma at the top of the file (see github discussion here). If you remove this line, it should compile fine:
/** #jsx h */ <--delete this line.
Parcel will automatically detect which JSX pragma to use for your project based on finding preact as a dependency in package.json (see documentation). You can also manually control it with a tsconfig.json file like this:
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "preact"
}
}
(This bug should probably still be fixed, but hopefully this is enough to help you work around it. I filed a github issue here).
Given the parent component, I am using a child component DynamicFieldSet that is a grouping of FormItems. But I am receiving the error:
Warning: validateDOMNesting(...): <form> cannot appear as a descendant of <form>. See CreateTopic > Form > form > ... > DynamicFieldSet > Form > form.
I have tried to remove the <Form> </Form> tags in my child component, but then it is a compile error.
Is there a way I can disable rendering of the child Form view?
Parent component
class CreateTopic extends React.Component {
render() {
return (
<div className="create-topic-container">
<h3>Create an event</h3>
<Form onSubmit={this.handleSubmit}>
<FormItem>...</FormItem>
<FormItem>...</FormItem>
<FormItem>...</FormItem>
<FormItem
{...formItemLayout}
label="Results"
style={{ marginBottom: SPACING_FORM_ITEM }}
>
{getFieldDecorator('results', {
rules: [
{
required: true,
message: 'Results cannot be empty.',
},
],
})(<DynamicFieldSet
form={this.props.form}
/>)}
</FormItem>
</Form>
</div>
);
}
}
DynamicFieldSet - Child component
export class DynamicFieldSet extends React.Component {
render() {
getFieldDecorator('keys', { initialValue: ['0', '1'] });
const keys = getFieldValue('keys');
const formItems = keys.map((k, index) => {
return (
<FormItem
{...formItemLayoutWithOutLabel}
required={false}
key={k}
>
{getFieldDecorator(`results[${k}]`, {
validateTrigger: ['onChange', 'onBlur'],
rules: [
{
required: true,
whitespace: true,
message: 'Result name cannot be empty.',
},
{
validator: this.validateLength,
},
],
})(<Input placeholder={`Result #${index + 1}`} style={{ width: '80%', marginRight: 8 }} />)}
{keys.length > 2 ? (
<Icon
className="dynamic-delete-button"
type="minus-circle-o"
disabled={keys.length === 1}
onClick={() => this.remove(k)}
/>
) : null}
</FormItem>
);
});
return (
<Form>
{formItems}
<FormItem {...formItemLayoutWithOutLabel}>
{keys.length < 10 ? (
<Button type="dashed" onClick={this.add} style={{ width: '80%' }}>
<Icon type="plus" />Add Result
</Button>
) : null}
</FormItem>
</Form>
);
}
}
I faced this issue when using ant design table and turns out its not ant design which throws the warning. It's the web standards description
"Every form must be enclosed within a FORM element. There can be several forms in a single document, but the FORM element can't be nested."
So, there should not be a form tag inside a form tag.
To solve the issue (in our case), remove the Form tag inside the DynamicFieldSet "return" and replace with a div tag
Hope it helps :)
You can portal a form like this:
import Portal from '#material-ui/core/Portal';
const FooComponent = (props) => {
const portalRef = useRef(null);
return <>
<form>
First form
<div ref={portalRef} />
</form>
<Portal container={portalRef.current}>
<form>Another form here</form>
</Portal>
</>;
}
In the example above I use the react material-ui Portal component. But you can try to implement it with React Portals as well
If you're using MUI, the Box component contains an attribute that identifies them as any native HTML container; form is one of them. E.g:
<Box
xs={6}
sx={{
"& > :not(style)": { m: 1, width: "25ch" },
}}
component="form"
noValidate
autoComplete="off"
>
In such case, we just need to delete that attribute, it will default to a DIV. The form will continue to work as expected, and the error will disappear off the console.
In my case this is occur bcoz of i declared <form> inside another <form/> tag.
This is a bit longwinded so I'll do my best to explain clearly.
I'm making a simple poll app and on the home page is an array of polls where you can vote on each poll.
Each poll is on a card and there will be different radio buttons representing the different voting options for that poll.
I'm trying to set up a form for each poll which contains radio button inputs for each of the different options and push that onSubmit to an action creator.
However, I would also like to pass that title of the poll as well as an argument to the action creator so that I can create a single action creator that will help me submit the votes for all the polls. Something like submitVote(title, option).
Here is my polls page:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actions from '../../actions';
import Loading from '../Loading';
class MyPolls extends Component {
constructor(props) {
super(props);
this.state = {
skip: 0,
isLoading: true,
isLoadingMore: false,
value: ''
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
componentDidMount() {
this.props.fetchMyPolls(this.state.skip)
.then(() => {
setTimeout(() => {
this.setState({
skip: this.state.skip + 4,
isLoading: false
});
}, 1000);
});
}
sumVotes(acc, cur) {
return acc.votes + cur.votes
}
loadMore(skip) {
this.setState({ isLoadingMore: true });
setTimeout(() => {
this.props.fetchMyPolls(skip)
.then(() => {
const nextSkip = this.state.skip + 4;
this.setState({
skip: nextSkip,
isLoadingMore: false
});
});
}, 1000);
}
handleSubmit(e) {
e.preventDefault();
}
handleChange(event) {
console.log(event.target.value);
this.setState({ value: event.target.value });
}
renderPolls() {
return this.props.polls.map(poll => {
return (
<div className='card' key={poll._id} style={{ width: '350px', height: '400px' }}>
<div className='card-content'>
<span className='card-title'>{poll.title}</span>
<p>Total votes: {poll.options.reduce((acc, cur) => { return acc + cur.votes }, 0)}</p>
<form onSubmit={this.handleSubmit}>
{poll.options.map(option => {
return (
<p key={option._id}>
<input
name={poll.title}
className='with-gap'
type='radio'
id={option._id}
value={option.option}
onChange={this.handleChange}
/>
<label htmlFor={option._id}>
{option.option}
</label>
</p>
)
})}
<button
type='text'
className='activator teal btn waves-effect waves-light'
style={{
position: 'absolute',
bottom: '10%',
transform: 'translateX(-50%)'
}}
>
Submit
<i className='material-icons right'>
send
</i>
</button>
</form>
</div>
<div className='card-reveal'>
<span className='card-title'>{poll.title}
<i className='material-icons right'>close</i>
</span>
<p>
dsfasfasdf
</p>
</div>
</div>
)
})
}
render() {
return (
<div className='center-align container'>
<h2>My Polls</h2>
{this.state.isLoading ? <Loading size='big' /> :
<div style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'space-evenly', alignItems: 'center', alignContent: 'center' }}>
{this.renderPolls()}
</div>}
<div className='row'>
{this.state.isLoadingMore ? <Loading size='small' /> :
<button
className='btn red lighten-2 wave-effect waves-light' onClick={() => this.loadMore(this.state.skip)}>
Load More
</button>}
</div>
</div>
);
}
}
function mapStateToProps({ polls }) {
return { polls }
}
export default connect(mapStateToProps, actions)(MyPolls);
Demo of the app so far: https://voting-app-drhectapus.herokuapp.com/
(use riverfish#gmail.com and password 123 to login).
Github repo: https://github.com/drhectapus/Voting-App
I'd like to program it so that when form is submitted via this.handleSubmit, the handleSubmit function can take 2 arguments, title and option and pass that onto an action creator in redux.
How do I do this?
It's a little difficult to understand everything going on here, but I get the sense that your main goal is to pass two args to this.handleSubmit. You may instead consider just passing poll.title and grabbing the selected option from state. Try something like this:
this.handleSubmit(title) {
// this.state.value should already have the selected option!
let obj = {
title,
option: this.state.value
};
// dispatch the object to redux, update your reducer, etc.
}
And in your render, be sure to bind poll.title as the argument:
render() {
...
<form onSubmit={this.handleSubmit.bind(this, poll.title)}>
}
Does that help at all? Let me know if I'm totally missing the mark on what you intend. With .bind() you pass the this context to use followed by a list of common separated args, so you could pass multiple args, but it's much easier to just grab option from state in this case.
Edit
If you want to access the SyntheticEvent that gets fired on submit, you simple specify it as the second argument to this.handleSubmit like so:
this.handleSubmit(title, event) {
// prevent form submit
event.preventDefault();
}
// this is the exact same as above, no need to pass event
render() {
...
<form onSubmit={this.handleSubmit.bind(this, poll.title)}>
}
In React, synthetic events are always passed as the last argument to a bound function and simply need to be specified to be in the method definition (no need to specify in render). This is Function.prototype.bind way of working with functions and events in React. Here are the supporting docs: https://reactjs.org/docs/handling-events.html#passing-arguments-to-event-handlers
I've just started trying out react after a few tutorials on Redux and React and I'm getting an error in the console:
Warning: Stateless function components cannot be given refs (See ref
"username" in FieldGroup created by Login). Attempts to access this
ref will fail.
What is the proper way to pass form field input values to my submit button? Should these values go into the redux store? After reading the docs: https://reactjs.org/docs/refs-and-the-dom.html#a-complete-example it seems like I should avoid refs in this case. So, without refs how do I get the input values to the submit button? Thanks for any help.
Login.jsx
import React, {Component, PropTypes} from 'react';
import {Row, Col, FormControl, FormGroup, ControlLabel, HelpBlock, Checkbox, Button} from 'react-bootstrap';
export default class Login extends Component {
render() {
const { errorMessage } = this.props;
function FieldGroup({ id, label, help, ...props }) {
return (
<FormGroup controlId={id}>
<ControlLabel>{label}</ControlLabel>
<FormControl {...props} />
{help && <HelpBlock>{help}</HelpBlock>}
</FormGroup>
);
}
const formInstance = (
<Col xs={12} md={8} mdOffset={2}>
<code><{'Col xs={12} md={8}'} /></code>
<form>
<FieldGroup
id="formControlsEmail"
type="email"
label="Email address"
placeholder="Enter email"
ref="username"
/>
<FieldGroup
id="formControlsPassword"
label="Password"
type="password"
ref="password"
/>
<Checkbox checked readOnly>
Checkbox
</Checkbox>
<Button type="submit" onClick={(event) => this.handleClick(event)}>
Submit
</Button>
{errorMessage &&
<p>{errorMessage}</p>
}
</form>
</Col>
);
return formInstance;
}
handleClick(event) {
const username = this.refs.username
const password = this.refs.password
const creds = { username: username.value.trim(), password: password.value.trim() }
this.props.onLoginClick(creds)
}
}
Login.propTypes = {
onLoginClick: PropTypes.func.isRequired,
errorMessage: PropTypes.string
}
Functional components in react (stateless) don't have refs.
From the official docs
Refs and Functional Components
You may not use the ref attribute on functional components because they don’t have instances:
Use an ES6 class instead if you need refs, if not use this.state from your Parent Login component with class syntax and use that instead with this.setState(yourState) when the input value changes on your FieldGroup
And then in your you would do
handleClick(event) {
const username = this.state.username
const password = this.state.password
const creds = { username: username.value.trim(), password: password.value.trim() }
this.props.onLoginClick(creds)
}
From the docs :
You can, however, use the ref attribute inside a functional component as long as you refer to a DOM element or a class component: