How can I fix the React Native setState error? - javascript

First of all, I apologize for my bad English.I am using the select component of the UI Kitten. Every item fails when I change it.
This is the mistake;
"Warning cannot update during an existing state react native"
I am sharing sample codes with you.
const data = [
"test 1",
"test 2"
];
constructor(props) {
super(props);
this.state = {
selectedIndex: new IndexPath(0),
displayValue: null
};
}
componentDidMount() {
this._handleSetSelectedIndex = this._handleSetSelectedIndex.bind(this);
}
_handleSetSelectedIndex(value) {
this.setState({selectedIndex: value});
}
in Render function;
<Select
style={{ width: 300 }}
placeholder='Default'
value={data[this.state.selectedIndex.row]}
selectedIndex={this.state.selectedIndex}
onSelect={index => this._handleSetSelectedIndex(index)}>
{data.map((key, value) => {
return (
<SelectItem key={value} title={key}/>
);
})}
</Select>

The problem is that the program can't update state inside rendering..it goes through infinite loop so "selectedIndex" must have an event handler function to handle when to setState it.

onSelect={index => this._handleSetSelectedIndex(index)}>
this line seems problematic to me.
try onSelect={this._handleSetSelectedIndex}
also add a console.log inside this function and see if it ever fires when you select a new item.
Also worth to try to reproduce the basic onSelect first following their tutorial,
<Select
selectedIndex={selectedIndex}
onSelect={index => setSelectedIndex(index)}>
<SelectItem title='Option 1'/>
<SelectItem title='Option 2'/>
<SelectItem title='Option 3'/>
</Select>

When you call setSelectedIndex, you pass the index, but it is the event object. Use this syntaxis:
onChange={(event) => this._handleSetSelectedIndex(event)}>
See full example in the playground: https://jscomplete.com/playground/s524798

Related

React PropTypes warning for function component with a prop that has a default value [duplicate]

I want to get option's value and key when select onChange.
I know I can get option's value simplicity used event.target.value in onChangeOption function, but how can I get key?
<select onChange={this.onChangeOption} value={country}>
{countryOptions.map((item, key) =>
<option key={key} value={item.value}>
{item.name}
</option>
)}
</select>
You will need to pass value in key as a different prop.
From docs:
Keys serve as a hint to React but they don’t get passed to your components. If you need the same value in your component, pass it explicitly as a prop with a different name:
Read: https://reactjs.org/docs/lists-and-keys.html
Passing key as a prop works obviously, but much quicker way could be to include the key as a custom attribute in your html.
class App extends React.Component {
constructor(props) {
super(props);
this.onSelect = this.onSelect.bind(this);
}
onSelect(event) {
const selectedIndex = event.target.options.selectedIndex;
console.log(event.target.options[selectedIndex].getAttribute('data-key'));
}
render() {
return (
<div>
<select onChange = {this.onSelect}>
<option key="1" data-key="1">One</option>
<option key="2" data-key="2">Two</option> </select>
</div>
);
}
}
ReactDOM.render( < App / > , document.getElementById('root'));
<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="root"></div>
A simple way of doing this is:
const functionCall = (event) => {
console.log(event.target.getAttribute('a-key'));
}
<button a-key={1} onClick={functionCall}>Press Me</button>
I put onClick but you can use any event.
You can pass anything through value. I think this solves your problem.
<select onChange={this.handleChange}>
{this.state.countryOptions.map((item, key) =>
<option key={key}
value={JSON.stringify({key, value:item.value})}>
//passing index along with value
{item.value}
</option>
)}
</select>
handleChange(e) {
let obj = JSON.parse(e.target.value)
// gives object{ key:country index, value: country value}
}
Imagine a component like this:
<Component data={[{id:'a23fjeZ', name="foo"}, ...]}`
Which renders a list of inputs, and gets in a data prop which is a collection (Array of Objects):
function Component ( props ){
// cached handler per unique key. A self-invoked function with a "cache" object
const onChange = (cache => param => {
if( !cache[param] )
cache[param] = e =>
console.log(param, e.target.name, e.target.value)
return cache[param];
}
)({});
return props.data.map(item =>
<input key={item.id} name={item.name} onChange={onChange(item.id)} />
}
}
As you can see, the key isn't being accessed, but is passed into a currying function which is handling the caching internally, to prevent "endless" creation of functions on re-renders.
I'm a beginner and have battle with this for days now!
Now, I will be happy to share you my final answer!
This is really easy -All you got to do is to declare an anonymous function! for an onClick on the item you wanna detect its key!
e.g
data.map((item) => (<div
className = "newItem" key = {item.id} onClick = {() = displayKey(item.id)}
>{item.value}</div>))
then your function can be thus;
will display any "val" passed in
const displayKey = (val) => {
console.log(val)
}
I hope I was able to help!
battling with a problem can be a pain in the ass!
Kind Regards .
So you do need to pass the key into the <option> as a prop, but since the <select> is a native field and handles changes internally it gets a little hard to get that key value back out.
Lets start one issue at a time, passing the prop into the option could be as simple as wrapping the option component like so and using the index prop as the new key prop.
const CustomOption = ({ value, children, index }) => (
<option key={index} value={value}>{value}</option>
);
Also note that we're creating a custom component wrapper above to swallow the index prop from being applied to <option /> itself as react doesnt like unknown props on dom elements.
Now if we could handle the selected event inside the option we would be done, but we can't do that. so we need to make a custom select as well:
class CustomSelect extends React.Component {
static propTypes = {
value: PropTypes.object,
onChange: PropTypes.func,
}
handleChanged = (e) => {
const newValue = e.target.value;
let newKey;
// iterate through our children searching for the <CustomOption /> that was just selected
React.children.forEach(this.children, (c) => {
if (c.props && c.props.index && c.props.value === newValue) {
newKey = c.props.key;
}
});
this.props.onChange(e, newKey);
}
render() {
return (
<select value={this.props.value} onChange={this.handleChanged}>
{this.props.children}
</select>
);
}
}
it has two props value, and onChange. Notice that we intercept the change event in order to find the index (the key) and we pass it along to the parent as the second parameter. This is not very nice but I can't think of another easy way to do this while still using the native <select> element.
Note you need to replace your usages of <select> and <optoin> to use these new classes, and assign the index prop along with the key prop on the option.

React "Maximum update depth exceeded."

I have a container which is a child container. This child container handles states and it also receives some props from the parent. I have two select statements which onChange sets state in the child container. For some reason, the setState() is causing the container to re render. The weird part is that the render() and also my setState() code is called only once. (I added debugger to check). Please find my Select combobox code below:
<Select
native
name="evidenceNode"
value={nodeType}
onChange={this.handleChange('nodeType')}
className={classes.textField}
>
<option />
{NODE_TYPE.map(function (item, i) {
return <option key={i} value={item}>{item}</option>
})}
</Select>
<Select
native
name="modelType"
value={modelType}
onChange={this.handleChange('modelType')}
className={classes.textField}
>
<option />
{MODEL_TYPE.map(function (item, i) {
return <option key={i} value={item}>{item}</option>
})}
</Select>
Here is my handleChange function:
handleChange = name => event => {
this.setState({
[name]: event.target.value,
});
};
I am suspecting that this is a very small fix but I don't know where am I going wrong.
Things I have tried:
Change the way I am calling handle change to a arrow function and it didnt work
I removed one of the Select statement and tried running again and it worked.
I tried to remove the handleChange call from one of the Select statement and it worked fine.
Also I should mention: I have a componentWillReceiveProps function (Not sure if it matters)
componentWillReceiveProps(nextProps, nextContext) {
if(nextProps.selectedNode !== this.state.selectedNode){
this.setState({
selectedNode: nextProps.selectedNode
});
}
}
The issue is onChange={this.handleChange('modelType')}.
You're not attaching an event, you're calling a method with that.
You're entering in an infinite loop because of that.
this.handleChange('modelType') occurs a re-render which call again this.handleChange('modelType')...etc
Attach on the onChange event an anonymous function which call handleChange
onChange={e => this.handleChange('modelType', e)}
And change handleChange params declaration :
handleChange = (name, event) => {}
The problem wasn't with the handleChange listener. Apparently [Material-UI]https://material-ui.com/ (The tool I am using for the form elements) required us to add a FormControl element above every Select I add. So the component should look something like this.
<FormControl
// className={classes.formControl}
>
<Select
native
name="nodeType"
value={nodeType}
onChange={this.handleChange('nodeType')}
className={classes.textField}
inputProps={{
name: 'nodeType',
id: 'nodeType'
}}
>
<option/>
{NODE_TYPE.map(function (item, i) {
return <option key={i} value={item}>{item}</option>
})}
</Select>
</FormControl>
The mistake I made was I had one FormControl and it had two Select elements inside it. This particular thing isn't documented properly on their website.
I think Material-UI recursively calls handleChange on every Select component inside the Form control and since I had more than one, it was going into a loop. I hope this helps.

How to add the input field inside the select option using ant design and react

I created select option using ant design .But I need create editable cell inside the select option.
This my select option code
<Select
showSearch
style={{ width: 400 }}
placeholder="Select a Bank"
optionFilterProp="children"
onChange={this.handleChange.bind(this)}
>
<option value="1">Bank1</option>
<option value="2"> Bank2</option>
<option value="3"> Bank3</option>
</Select>
And onChange functions is
handleChange(value) {
console.log(`selected ${value}`);
this.setState({
bank:value,
});
}
Can you help me?
I suppose the question is whether or not this is an editable list.
The Select component has a mode prop that can be used to change the functionality with the following options:
'default' | 'multiple' | 'tags' | 'combobox'
Using the tags mode would allow you to add and remove items and generate a tokenized list when the form is submitted.
If you are looking at a fixed list and then wanting to create new items to add to the list:
If you want to be able to add new items to the list, this doesn't exist currently, as far as I am aware.
You may be able to refashion something from the Ant Design Pro components, or otherwise come up with a solution where:
when "create" is selected, you toggle the Select for an Input
when the input is submitted/blurred update the Options list, toggle the Select/Input once more and submit the value to the back-end.
I hope this helps.
You don't need to do that actually. All you need to do is to use component state and two simple callback functions ant design provides for select.
So let's assume you need to allow users not to also search for existing values inside a Select but if it didn't exist they can choose a new one. So here's what I'd do:
Inside render() method:
<Select
showSearch
value={this.title}
filterOption={true}
onSearch={this.handleSearch}
onFocus={this.handleFocus}
style={{ width: "100%" }}>
{this.titles.map((title) => (
<Select.Option key={title}>{title}</Select.Option>
))}
</Select>
Where this.titles = ["something", "else"].
Then Inside this.handleSearchand this.handleFocus I'd write:
protected handleSearch = (value: string) => {
this.setState({ titles: value && value !== "" ? [...this.titles, value] : fileTitles });
};
protected handleFocus = () => {
this.setState({ this.titles });
};
What we're basically doing is to populate the options we're iterating over inside the Select with this.titles in the state of the component itself (don't confuse it with Redux or MobX) when user opens the selector and once user searches for anything that would be added to options as well. With this approach you won't need an input or a switch to show/hide inputs. Hope it helps.
You could use another modal to input the additional value.
Check this : https://codesandbox.io/s/antdselectaddoption-7fov7
Code from mamsoudi throws Errors, so i took his idea and made my own component that i'm sharing with you.
import React from 'react';
import {Select} from "antd";
class FieldSelectAndCustomText extends React.Component {
constructor(props) {
super(props);
this.initialTitles = ["something", "else"];
this.state = {
titles: this.initialTitles,
currentValue: null,
};
}
handleSearch = (value) => {
const titles = this.state.titles;
for (let i = 0; i < titles.length; i++) {
const isSearchValueInState = new RegExp(value).test(titles[i]);
if (!isSearchValueInState) {
this.setState({
titles: [...this.initialTitles, value],
currentValue: value
});
break;
}
}
};
handleChange = (value) => {
this.setState(prev => ({...prev, currentValue: value}));
}
render () {
return (
<div>
<Select
showSearch
value={this.state.currentValue}
filterOption={true}
onSearch={this.handleSearch}
onChange={this.handleChange}
onFocus={this.handleFocus}
style={{ width: "100%" }}>
{this.state.titles.map((title) => (
<Select.Option value={title} key={title}>{title}</Select.Option>
))}
</Select>
</div>
);
}
}

How to get "key" prop from React element (on change)?

I want to get option's value and key when select onChange.
I know I can get option's value simplicity used event.target.value in onChangeOption function, but how can I get key?
<select onChange={this.onChangeOption} value={country}>
{countryOptions.map((item, key) =>
<option key={key} value={item.value}>
{item.name}
</option>
)}
</select>
You will need to pass value in key as a different prop.
From docs:
Keys serve as a hint to React but they don’t get passed to your components. If you need the same value in your component, pass it explicitly as a prop with a different name:
Read: https://reactjs.org/docs/lists-and-keys.html
Passing key as a prop works obviously, but much quicker way could be to include the key as a custom attribute in your html.
class App extends React.Component {
constructor(props) {
super(props);
this.onSelect = this.onSelect.bind(this);
}
onSelect(event) {
const selectedIndex = event.target.options.selectedIndex;
console.log(event.target.options[selectedIndex].getAttribute('data-key'));
}
render() {
return (
<div>
<select onChange = {this.onSelect}>
<option key="1" data-key="1">One</option>
<option key="2" data-key="2">Two</option> </select>
</div>
);
}
}
ReactDOM.render( < App / > , document.getElementById('root'));
<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="root"></div>
A simple way of doing this is:
const functionCall = (event) => {
console.log(event.target.getAttribute('a-key'));
}
<button a-key={1} onClick={functionCall}>Press Me</button>
I put onClick but you can use any event.
You can pass anything through value. I think this solves your problem.
<select onChange={this.handleChange}>
{this.state.countryOptions.map((item, key) =>
<option key={key}
value={JSON.stringify({key, value:item.value})}>
//passing index along with value
{item.value}
</option>
)}
</select>
handleChange(e) {
let obj = JSON.parse(e.target.value)
// gives object{ key:country index, value: country value}
}
Imagine a component like this:
<Component data={[{id:'a23fjeZ', name="foo"}, ...]}`
Which renders a list of inputs, and gets in a data prop which is a collection (Array of Objects):
function Component ( props ){
// cached handler per unique key. A self-invoked function with a "cache" object
const onChange = (cache => param => {
if( !cache[param] )
cache[param] = e =>
console.log(param, e.target.name, e.target.value)
return cache[param];
}
)({});
return props.data.map(item =>
<input key={item.id} name={item.name} onChange={onChange(item.id)} />
}
}
As you can see, the key isn't being accessed, but is passed into a currying function which is handling the caching internally, to prevent "endless" creation of functions on re-renders.
I'm a beginner and have battle with this for days now!
Now, I will be happy to share you my final answer!
This is really easy -All you got to do is to declare an anonymous function! for an onClick on the item you wanna detect its key!
e.g
data.map((item) => (<div
className = "newItem" key = {item.id} onClick = {() = displayKey(item.id)}
>{item.value}</div>))
then your function can be thus;
will display any "val" passed in
const displayKey = (val) => {
console.log(val)
}
I hope I was able to help!
battling with a problem can be a pain in the ass!
Kind Regards .
So you do need to pass the key into the <option> as a prop, but since the <select> is a native field and handles changes internally it gets a little hard to get that key value back out.
Lets start one issue at a time, passing the prop into the option could be as simple as wrapping the option component like so and using the index prop as the new key prop.
const CustomOption = ({ value, children, index }) => (
<option key={index} value={value}>{value}</option>
);
Also note that we're creating a custom component wrapper above to swallow the index prop from being applied to <option /> itself as react doesnt like unknown props on dom elements.
Now if we could handle the selected event inside the option we would be done, but we can't do that. so we need to make a custom select as well:
class CustomSelect extends React.Component {
static propTypes = {
value: PropTypes.object,
onChange: PropTypes.func,
}
handleChanged = (e) => {
const newValue = e.target.value;
let newKey;
// iterate through our children searching for the <CustomOption /> that was just selected
React.children.forEach(this.children, (c) => {
if (c.props && c.props.index && c.props.value === newValue) {
newKey = c.props.key;
}
});
this.props.onChange(e, newKey);
}
render() {
return (
<select value={this.props.value} onChange={this.handleChanged}>
{this.props.children}
</select>
);
}
}
it has two props value, and onChange. Notice that we intercept the change event in order to find the index (the key) and we pass it along to the parent as the second parameter. This is not very nice but I can't think of another easy way to do this while still using the native <select> element.
Note you need to replace your usages of <select> and <optoin> to use these new classes, and assign the index prop along with the key prop on the option.

ReactJS: Maintain data state between parent and child

There's a parent, <MessageBox /> element, which contains a list of messages stored in its state. For each message in messages, a <Message /> element is created inside <MessageBox /> which has fields for message.subject and message.body. A user can edit the message.subject and message.body and once done, the message object is sent back to <MessageBox /> through a props.updateHandler() to maintain the message state in the parent.
In my current approach, I'm storing the message data in MessageBox's state and in the render() function, I'm creating the <Message /> elements and passing a callback to each of them to send back data changes. In the callback, the updated data from each of the <Message /> elements is updated back into MessageBox's state. The reason for this is to keep all the recent updated data in one place only. The above approach creates havoc if shouldComponentUpdate() method in <Message /> is not overloaded (infinite recursion).
Is there a better approach for this? I've to write a lot of code just to override the builtin methods to keep the entire thing stable. As I'm not planning to go for Flux/Redux, is there a React-only approach for this?
EDIT: Since there's a lot of confusion, I'm adding minimal code.
class Message extends React.Component {
constructor(props) {
this.state = {
subject: this.props.subject,
body: this.props.body,
type: this.props.type,
messageIndex: this.props.messageIndex
};
}
componentDidUpdate() {
this.props.updateHandler(messageIndex, {
subject: this.state.subject,
body: this.state.body,
type: this.state.type
});
}
render() {
return (
<div>
<input
type="text"
defaultValue={this.state.subject}
onBlur={e => this.setState({subject: e.target.value})} />
<input
type="text"
defaultValue={this.state.subject}
onBlur={e => this.setState({body: e.target.value})} />
<select
type="text"
value={this.state.subject}
onChange={e => this.setState({type: e.target.value})}>
<option>Type 1</option>
<option>Type 2</option>
</select>
</div>
)
}
}
class MessageBox extends React.Component {
constructor(props) {
this.state = {
messages: aListOfMessageObjects
}
}
updateHandler(message) {
// Message update happens here and returns a list updatedMessages
this.setState({
messages: updatedMessages
});
}
render() {
let _this = this;
var messagesDOM = this.state.messages.map((m) => {
return (
<Message
message={m}
updateHandler={_this.updateHandler.bind(_this)} />
);
})
return (
<div>
{messagesDOM}
</div>
);
}
}
If that can help, read thinking-in-react. It explains how data should go only one way to avoid be lost in UI updates.
React ToDo MVC will provide you an example of React good practice on a real case
To know how to pass props from your parent to children read controlled-components. you'll have to use value and onBlur on each input. Any onBlur event will call this.props.updateHandler with e as parameter instead of e => this.setState({type: e.target.value}).
Don't do a callback to MessageBox from componentDidUpdate() of Message. Do a callback directly from an action in Message.
You don't need state in Message component at all. Props will keep the values you are interested if you update parent's state properly.
What you need is something like:
<input type="text"
defaultValue={this.props.subject}
onBlur={e => this.updateSubject(e.target.value)} />
updateSubject: function(newSubjectValue) {
this.props.updateHandler(messageIndex, {
subject: newSubjectValue,
body: this.props.body,
type: this.props.type
});
}
That way the component will get re-rendered, but won't do another call to the parent's setState.

Categories

Resources