I am using react-bootstrap-validation that decorates the react-bootstrap Input tag.
The ValidatedInput requires that it is inside a Form component.
When I add my ValidatedInput to a custom sub component I get an error saying it needs to be inside a Form which it is, but I guess it is further down the tree now so can not see the Form.
Is there a way of referencing the parent Form so the ValidatedInput can see the parent.
Looking at the source of the Validation lib I can see that the ValidationInput needs to register to the Form but am not sure how to do this from the sub component.
// Parent render
render(){
<Form
className="fl-form fl-form-inline fl-form-large"
name="customer-details"
onValidSubmit={this._handleValidSubmit}
onInvalidSubmit={this._handleInvalidSubmit}
validationEvent='onChange'>
<TitleSelect handleChange={this.updateDropDown} value={this.state.form.title} />
</form>
}
// Sub class containing the ValidatedInput
export class TitleSelect extends React.Component {
static propTypes = {
handleChange: React.PropTypes.func.isRequired,
value: React.PropTypes.string.isRequired
}
render(){
return (
<ValidatedInput
name="title"
label='title'
type='select'
value={this.props.value}
onChange={this.props.handleChange}
groupClassName='form-group input-title'
wrapperClassName='fl-input-wrapper'
validate='required'
errorHelp={{
required: 'Please select a title.'
}}>
<option value="" ></option>
<option value="Mr">Mr</option>
<option value="Mrs">Mrs</option>
<option value="Master">Mstr.</option>
<option value="Ms">Ms</option>
<option value="Miss">Miss</option>
<option value="Reverend">Rev.</option>
<option value="Doctor">Dr.</option>
<option value="Professor">Prof.</option>
<option value="Lord">Lord</option>
<option value="Lady">Lady</option>
<option value="Sir">Sir</option>
<option value="Master">Mstr.</option>
<option value="Miss">Miss</option>
</ValidatedInput>
)
}
};
At the moment this is impossible to do. It will be possible in a future release once we get proper parent-based contexts in react and I will migrate the component to contexts. But for now I would recommend to split your render() method to couple of smaller ones and reuse them.
Sa #Ваня Решетников said above it's impossible to do it now because of limitations of current design. A solution I went for is this.
Convert subcomponent to plain JS object
TitleSelect = {
// move prop types to parent
renderTitleSelect(){
...
}
}
Add new object as a mixin to parent and render a function
mixins: [TitleSelect],
...
render() {
<Form ...>
// parentheses are important!
{this.renderTitleSelect()}
</Form>
}
Related
I have this component that I use to only show a select element when a condition is met in another component. (it looks something like userIsAdmin ? <thisComponent/> : null)
I have a variable which I export as adminSelectBase which is the selected option. Is there instead another way to pass either a state variable (from useState) or just some data from the child to the parent?
alternatively, could I somehow pass a reference of a state or variable to the child component? something like: <thisComponent myVariable={variable} setMyVariable={setMyVariable}/>?
import React from 'react';
import "./createinquiry.css";
export let adminSelectBase = false;
export default function AdminBaseSelectComponent(props) {
return (
<>
<select className="Create-Inquiry-input" onChange={(e) => adminSelectBase = e.target.value}>
<option value="1">1</option>
<option value="4">4</option>
<option value="6">6</option>
<option value="8">8</option>
<option value="10">10</option>
<option value="22">22</option>
<option value="25">25</option>
<option value="28">28</option>
<option value="30">30</option>
</select><br/>
</>
)
}
EDIT:
usage of this component:
{isAdmin ? <AdminBaseSelectComponent/> : null}
this select component changes a stateful variable:
[selectedBase, setSelectedBase] = useState("");
How can I change the initial state value of a field based on the input value of another field?
Here's my code:
import React from "react";
export class someComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
grade: "",
tuition: changeStateHere
};
}
render() {
<div>
<form onSubmit={this.someFunction.bind(this)}>
<select name="grade" onChange={this.onChange} value={this.state.grade}>
<option value="">Class</option>
<option value="Grade One">Grade One</option>
<option value="Grade Two">Grade Two</option>
<option value="Grade Three">Grade Three</option>
<option value="Grade Four">Grade Four</option>
<option value="Grade Five">Grade Five</option>
<option value="Grade Six">Grade Six</option>
</select>
<input
type="text"
name="tuition"
placeholder="Tuition"
value={this.state.tuition}
onChange={this.onChange}
/>
</form>
</div>;
}
}
I want to set a dynamic initial state value for tuition based on input option on the grade select field.
For example, if a user selects Grade One, the tuition value should be 15000; if Grade Two, the value should be '20000'; etc.
Is there any workaround dynamically changing the initial state value?
Everything inside of the constructor is run before render. You're not going to be able to set a different initial value to the state based on something inside the render method.
Also move away from binding functions in the render method. it would be better to use a class method someFunction = () => {} and then attach it inside the JSX as onSubmit={this.someFunction}
Each time the component re-renders another instance of someFunction is bound which will eventually lead to performance issues in the browser.
You need to handle the grade selection and evaluate the event.target.value of the select element. Based on this you can use a switch to this.setState both Tuition and whether or not the input field is disabled. You should use defaultValue instead of value so that when the user picks and option it sets the input to this.state.tuition
import React from "react";
export class someComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
grade: "",
gradeSelected: false,
tuition: 0,
};
}
handleGradeSelect = event => {
let newState = Object.assign({}, this.state) // duplicate state so you can mutate this copy
switch(event.target.value){ // evaluate the value of the select option
case 'Grade One':
newState.tuition = 15000
case 'Grade Two':
newState.tuition = 20000
// You get the picture
default:
newState.tuition = 0 // needs to have a default if no option
}
newState.gradeSelected = true
this.setState(newState)
}
render() {
<div>
<form onSubmit={this.someFunction.bind(this)}>
<select name="grade" onChange={event => this.handleChange(event)}>
<option disabled>Class</option>
<option value="Grade One">Grade One</option>
<option value="Grade Two">Grade Two</option>
<option value="Grade Three">Grade Three</option>
<option value="Grade Four">Grade Four</option>
<option value="Grade Five">Grade Five</option>
<option value="Grade Six">Grade Six</option>
</select>
<input
type="text"
name="tuition"
placeholder="Tuition"
value={this.state.tuition}
onChange={this.onChange}
disabled={!this.state.gradeSelected}
/>
</form>
</div>;
}
}
I want to make a selection from a drop-down, query movies based on the selected value, map through and render them inside a react component but I got this error
A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.
Here's the code:
...
constructor(props) {
super(props)
this.state = { description: '', list: [] }
}
handleList() {
axios.get('https://facebook.github.io/react-native/movies.json')
.then(resp => this.setState({...this.state, list: resp.data})) // Trás a lista atualizada e zera a descrição
console.log(this.state.list)
}
render() {
const loop = this.state.list.map((list) => {
return(
<Grid cols='2'>
<select className="form-control" key={i}>
<option value="teste" onClick={() => this.handleList()}>{list.description}</option>
<option value="lime">Lime</option>
<option value="coconut">Coconut</option>
<option value="mango">Mango</option>
</select>
</Grid>
)
});
}
There is a couple of issues with this code example.
You are not returning anything inside the render() function. (This is the main issue you are having, or at least that React is crying about)
You are, well at least initially, mapping over an empty array and
The callback of this map you are doing things like list.description even though there exists no list and no description inside of list during the initial render.
You are using the onClick for the options instead of onChange
The endpoint you are requesting returns an object and you try to map through it. This should error because map is not defined as a function of an object.
I have managed to fix the issues I have mentioned including the one you are currently having. To save energy, typing the long explanation and reason that went into fixing your issue, have a look at this demo. It contains the exact same code you have posted, but of course, fixed/working.
I think onclick event is not suitable to use in option. Better use it on select
` <select value={this.state.value} onChange={this.handleChange}>
<option value="grapefruit">Grapefruit</option>
<option value="lime">Lime</option>
<option value="coconut">Coconut</option>
<option value="mango">Mango</option>
</select>
`
Sicne you are rendering a list of forms to reuse the same function, I always use a closure
handleChange = (id) = () => axios
.get(`$URL/id`)
.then(r => r.data)
.then(this.setState)
render() {
return (
list.map(item => <select
key={item.id}
onChange={this.handleChange(item.id)}
>
<option value="grapefruit">Grapefruit</option>
<option value="lime">Lime</option>
<option value="coconut">Coconut</option>
<option value="mango">Mango</option>
</select>))}
<select>
<option selected disabled>Select</option
I added an extra option with a disabled property, but this gives me the following warning:
Use the defaultValue or value props on select instead of setting
selected on option.
<select>
{this.props.optionarray.map(function(e){
return <option
value={e[self.props.unique]}
key={e[self.props.unique]}
>
{e[self.props.name]}
</option>
})}
</select>
optionarray is an array which is passed through props to be mapped on each index which has an object and I pass the key through props as well which was in the array. Everything is working here, it's just showing me the warning that I mentioned above.
How do I remove that or how do I set a perfect placeholder for a dropdown in react?
Reason is, React provide a better way of controlling the element by using states, so instead of using selected with option use the value property of select and define its value in the state variable, use the onChange method to update the state, Use it in this way:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: '1'
};
}
render(){
return(
<select value={this.state.value} onChange={(e)=>{this.setState({value: e.target.value})}}>
<option value='1' disabled>Select</option>
{
[2,3,4].map((i,j)=>{
return <option key={i} value={i}>{i}</option>
})
}
</select>
);
}
}
How to set placeholder for dropdown?
According to Mozilla Dev Network, placeholder is not a valid attribute on a <select>. So in place of that you can you this also to render a default option:
<option default>Select</option>
Use of key as per DOC:
Keys help React identify which items have changed, are added, or are
removed. Keys should be given to the elements inside the array to give
the elements a stable identity.
Suggestion:
Whenever we create any ui dynamically in loop, we need to assign a unique key to each element, so that react can easily identify the element, it basically used to improve the performance.
Check the working example:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: '1'
};
}
render(){
return(
<select value={this.state.value} onChange={(e)=>{this.setState({value: e.target.value})}}>
<option value='1' disabled>Select</option>
{
[2,3,4].map((i,j)=>{
return <option key={i} value={i}>{i}</option>
})
}
</select>
);
}
}
ReactDOM.render(<App />, document.getElementById('container'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='container'/>
Check the fiddle for working example: https://jsfiddle.net/29uk8fn0/
Functional Component
import { useState } from "react";
export default function DropdownComponent(props) {
const [formData, setFromData] = useState({
anyInput: "something",
dropdownInput: null,
});
return (
<>
anyInput: { formData.anyInput }
dropdownInput: { formData.dropdownInput }
<select name={"dropdownName"} onChange={ ({target})=> {
setFromData((prev)=> ({...prev, dropdownInput:target?.value}) );
}
}>
<option key='blankKey' hidden value >Placeholder Text for Select</option>
{
[2,3,4].map((i,j)=>{
return <option key={i} value={i}>{i}</option>
})
}
</select>
</>
)
}
I am trying to migrate my application from React 0.12 to React 0.14 and am having trouble with option elements that use react-intl FormattedMessage objects placed inside select tags.
Here is a sample JSX code:
<select>
<option value="value1"><FormattedMessage message={this.getIntlMessage('key1')}/></option>
<option value="value2"><FormattedMessage message={this.getIntlMessage('key2')}/></option>
</select>
This code works fine in React 0.12 and I see my translated option elements.
In react 0.14, I got this error:
Only strings and numbers are supported as <option> children.
I traced the message to this changeset in React that happened earlier this year:
https://github.com/facebook/react/pull/3847/files
How can I fix this issue? I can't be the only one trying to use internationalized option elements?
This has always been an issue. React < 0.14 used to silently accept invalid DOM structure, in your case <span> elements inside <option> elements. The browser would then correct the DOM structure, and cause the virtual DOM managed by React to be out of sync with the real thing. You wouldn't see errors until you tried to re-render existing components instead of just re-mounting them.
react-intl V2.0.0, which will ship with support for React 0.14, allows you to use the Function-As-Child pattern to customize the way your Formatted* components render. See the "Function-As-Child Support" paragraph on this issue.
In your case, you would do:
<FormattedMessage message={this.getIntlMessage('key1')}>
{(message) => <option value="value1">{message}</option>}
</FormattedMessage>
<FormattedMessage message={this.getIntlMessage('key2')}>
{(message) => <option value="value2">{message}</option>}
</FormattedMessage>
I don't think there's a way to achieve this on the current stable version, 1.2.1.
I had the same problem and solved it via the injectIntl().
This function is used to wrap a component and will inject the intl context object created by the IntlProvider as a prop on the wrapped component. Using the HOC factory function alleviates the need for context to be a part of the public API.
That means all you have to do is wrap your component with the injectIntl function, like that:
import React, {Component, PropTypes} from 'react';
import {defineMessages, injectIntl, intlShape} from 'react-intl';
const messages = defineMessages({
firstoption: {
id: 'mycomponent.firstoption',
defaultMessage: 'Coffee',
},
secondoption: {
id: 'mycomponent.secondoption',
defaultMessage: 'Tea',
}
});
class MyComponent extends Component {
render() {
const {formatMessage} = this.props.intl;
return (
<div>
<select>
<option value="value1">{formatMessage(messages.firstoption)}</option>
<option value="value2">{formatMessage(messages.secondoption)}</option>
</select>
</div>
);
}
}
MyComponent = {
intl : intlShape.isRequired
};
export default injectIntl(MyComponent)
Hope that helps...
As a little bit better alternative to #Alexandre Kirszenberg answer, it's also possible to inject intl object into component and use formatMessage function directly,
import { injectIntl, intlShape, defineMessages, FormattedMessage } from 'react-intl';
const AddressForm = ({ intl, street, number, postalCode, city, onChange }) => {
return (
<form id="paymentAddress">
// ...
<fieldset className="form-group">
<label htmlFor="country"><FormattedMessage { ...messages.country } />:</label>
<div>
<select name="country">
<option value="DE">{intl.formatMessage(messages.de)}</option>
<option value="UK">{intl.formatMessage(messages.uk)}</option>
<option value="CH">{intl.formatMessage(messages.ch)}</option>
</select>
</div>
</fieldset>
</form>
);
};
AddressForm.propTypes = {
intl: intlShape.isRequired,
// ...
}
With react-intl v4.0.0 you can do this:
<select
className="content"
name="type-enquiry"
defaultValue="Type of Enquiry"
onChange={handleChange}
required
>
<option name="options" disabled hidden>
Choose
</option>
<FormattedMessage id='contact.enquiry.a' key={'op' + '-' + 'a'}>
{(message) => <option value='a'>{message}</option>}
</FormattedMessage>
<FormattedMessage id='contact.enquiry.b' key={'op' + '-' + 'b'}>
{(message) => <option value='b'>{message}</option>}
</FormattedMessage>
<FormattedMessage id='contact.enquiry.c' key={'op' + '-' + 'c'}>
{(message) => <option value='c'>{message}</option>}
</FormattedMessage>
</select>
example code
Use injectIntl to wrap the component you want to use the API inside ,so you can use the API, such as formattedMessage and so on. See react-intl/wiki/API
"react-intl": "2.4.0",
"babel-plugin-react-intl": "2.4.0",
working snippet
<FormattedMessage
key={key}
id={popoverOptions[key].id}
defaultMessage={popoverOptions[key].defaultMessage}>
{(message) => <option value={key}>{message}</option>}
</FormattedMessage>
Whole Scenario
"reactstrap": "5.0.0-beta",
import { Input} from "reactstrap";
import {FormattedMessage} from "react-intl";
<Input
value={filter.status}
onChange={this.onFilterChange}
type="select"
name="select" id="exampleSelect">
{Object.keys(popoverOptions).map((key, index) => {
return (
<FormattedMessage
key={key}
id={popoverOptions[key].id}
defaultMessage={popoverOptions[key].defaultMessage}>
{(message) => <option value={key}>{message}</option>}
</FormattedMessage>
)
})}
</Input>