React Radio input selection persists on re-render - reactjs

I am rendering a component including radio inputs. When a radio input selection is made, and the change is updated in state (Zustand if it matters), the component re-renders.
However, the radio selection is persisted on the re-render. Why is this not being cleared on re-render? And/or how to I reset this?
Attached is a simplified case.
class TodoApp extends React.Component {
constructor(props) {
super(props)
this.state = {
count: 0,
}
}
update = () => {
this.setState({
count: 4
})
}
render() {
return (
<div>
<div>
<input id="one" type="radio" name="test"></input>
<label htmlFor="one">one</label>
</div>
<div>
<input id="two" type="radio" name="test"></input>
<label htmlFor="two">two</label>
</div>
<button onClick={this.update}>Update state from 0 to 4</button>
<h2>Count in state: {this.state.count}</h2>
</div>
)
}
}
ReactDOM.render(<TodoApp />, document.querySelector("#app"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>
============================================================
Update.
The issue was that I was outputting the radio buttons using map, and assigning the index key attribute.
const radioInputs = allItems.map((item, index) => {
return (
<div key={index}>
<input type="radio" ....../>
<label .....>{item.name}</label>
</div>
)
}
Because the same index was used on subsequent re-rendering, and React uses the key as sort of ID (docs), the next radio input with the same key was selected as the previous selection.
I just needed to use a "globally" unique key, as opposed to the index from map.

its actually Reacts beauty; in React we have virtual dom: abstraction layer on top of real dom. It consists of your React application's elements.
State changes in your application will be applied to the VDOM first. If the new state of the VDOM requires a UI change, the ReactDOM library will efficiently do this by trying to update only what needs to be updated.
For example, if only the attribute of an element changes, React will only update the attribute of the HTML element by calling document.setAttribute
When the VDOM gets updated, React compares it to to a previous snapshot of the VDOM and then only updates what has changed in the real DOM. If nothing changed, the real DOM wouldn't be updated at all. This process of comparing the old VDOM with the new one is called diffing.
thats why your radio buttons dont change when another state changes.
for more on this you can read here.

Related

Add element to a state React

I already have a state with this:
this.setState({
conversation:
(
<div>
{conversation.map(element => {
if (element.id === this.props.id) {
return(
<div className="row msg_container base_sent">
<div className="col-md-10 col-xs-10">
<div className="messages msg_sent">
<p>{element.message}</p>
</div>
</div>
</div>
)
}
else {
return(
<div className="row msg_container base_receive">
<div className="col-md-10 col-xs-10">
<div className="messages msg_receive">
<p>{element.message}</p>
</div>
</div>
</div>
)
}
})}
</div>
)
})
Now I would like to update it with new information. So add another div to it.
Something like that:
this.setState({conversation: previousConversation + new div})
How can I do it? Or I need to set a new state from zero
As per React docs (webarchive source):
What Should Go in State?
State should contain data that a component's event handlers may change to trigger a UI update. In real apps this data tends to be very small and JSON-serializable. When building a stateful component, think about the minimal possible representation of its state, and only store those properties in this.state. Inside of render() simply compute any other information you need based on this state. You'll find that thinking about and writing applications in this way tends to lead to the most correct application, since adding redundant or computed values to state means that you need to explicitly keep them in sync rather than rely on React computing them for you.
What Shouldn’t Go in State?
this.state should only contain the minimal
amount of data needed to represent your UI's state. As such, it should
not contain:
Computed data: Don't worry about precomputing values based on state —
it's easier to ensure that your UI is consistent if you do all
computation within render(). For example, if you have an array of list
items in state and you want to render the count as a string, simply
render this.state.listItems.length + ' list items' in your render()
method rather than storing it on state.
React components: Build them in render() based on underlying props and state.
Duplicated data from props: Try to use props as the source of truth where possible. One
valid use to store props in state is to be able to know its previous
values, because props can change over time.
In your case, move your html into your render function. Then store a condition or data in your state that would be used to trigger the addition of another div to your html either by conditionally rendering some html or by adding another div to an array inside a .map function based on adding more data to your state.
Example:
Class Example render React.Component{
state = {
comments: [
{ message:"comment 1", id: 1, timeStamp: "" },
{ message:"comment 2", id: 2, timeStamp: "" },
{ message:"comment 3", id: 3, timeStamp: "" }
]
}
componentDidMount = () => {
//update data from api...
.then(data => this.setState({ comments: data }))
}
render(){
conversation.map(element => {
if (element.id === this.props.id) {
return(
<div className="row msg_container base_sent">
<div className="col-md-10 col-xs-10">
<div className="messages msg_sent">
<p>{element.message}</p>
</div>
</div>
</div>
)
}
else {
return(
<div className="row msg_container base_receive">
<div className="col-md-10 col-xs-10">
<div className="messages msg_receive">
<p>{element.message}</p>
</div>
</div>
</div>
)
}
})
}
}
I don't think it's a good idea to store jsx components in the state of a component. I think you should only save the data in the state needed to render the component.
If you really want to store jsx in the state, why won't you define your 'conversation' property as array? Then you'll be able to add new components to it.
this.setState({
conversation: [
(<div>first</div)
]
});
const currentConversation = state.conversation;
currentConversation.push((<div>new div</div>));
this.setState({conversation: currentConversation})
But better to only store the data ie 'first' and 'new div'

Unnecessary DOM update for dynamic React elements

I have a React project that generates some DOM elements "dynamically" within JSX:
<div className='ui form'>
<h2 className="header">{subtype}</h2>
{
subtypes[subtype].fields.map((field) =>
<div className='field' key={field.name}>
<label>{field.label}</label>
<input name={field.name}
value={entity[field.name]}
onChange={onInputChange}/>
</div>
)
}
</div>
For a specific component, the generated input fields don't ever change during the life of the application (only their props change), so it is just a way to generate forms that are actually static.
So it is exactly equivalent to this "static" JSX:
<div className='ui form'>
<h2 className="header">{subtype}</h2>
<div className='field' key='field1'>
<label>Field 1</label>
<input name='field1'
value={entity['field1']}
onChange={onInputChange}/>
</div>
<div className='field' key='field2'>
<label>Field 2</label>
<input name='field2'
value={entity['field2']}
onChange={onInputChange}/>
</div>
</div>
If I used the first code snippet, then the HTML DOM elements get recreated on every change to state / props. If I use the second snippet, then the HTML appears to be unchanged and only the field values are updated (React can detect in the second instance that the virtual DOM elements are still the same, but not in the first instance)
Is there a way for me to create the "dynamic" virtual DOM in the first code example in a way that it can be cached and reused so that React sees it as being the same on each render?
Many thanks
Where is subtypes coming from? From what I understand you are receiving this in the component's props. If that is the case, you need to store this variable in this component's state. Then, you need to update it's state in it's componentWillReceiveProps lifecycle function.
The thing is, your component will only re-render when it's setState function is called. Hence, the components will not re-render when it's props change (after it has already been mounted).
class SimpleCom extends React.Component {
constructor(props) {
super(props);
this.state = {
subtypes: props.subtypes
}
}
componentWillReceiveProps(props) {
this.setState({
subtypes: props.subtypes
});
}
render() {
const subtypes = this.state.subtypes;
return (
<div className='ui form'>
<h2 className="header">{subtype}</h2>
{
subtypes[subtype].fields.map((field) =>
<div className='field' key={field.name}>
<label>{field.label}</label>
<input name={field.name}
value={entity[field.name]}
onChange={onInputChange}/>
</div>
)
}
</div>
);
}
}

Ace Editor clearing on react render

I have the following react structure (I simplified it for this post):
<div>
<div>
<div
<AceEditor/>
</div>
</div>
<div>
<p/>
<hr/>
{items}
</div>
</div>
AceEditor is imported from the react-ace npm package and {items} is an array of varying size, created from an array in this.state.
Everything works as it should except for one thing: Every time this structure is re-rendered due to changes in {items} (because of changes in the array in this.state), the text in the AceEditor is reset. No onChange event is fired and I can't seem to track the problem down to its roots.
Does anybody know what causes the problem and how to solve this?
Changes to the state will cause render to re-display the DOM and thus wipe out your changes anytime state is updated.
You will most likely need to store the status of AceEditor in state so it will re-display when the DOM re-renders.
function onChange(newValue) {
// store this value in state!!
this.setState({ newValue: newValue});
}
// Render editor
render(
<AceEditor
mode="java"
theme="github"
onChange={onChange}
name="UNIQUE_ID_OF_DIV"
value={this.state.newValue}
editorProps={{$blockScrolling: true}}
/>,
document.getElementById('example')
);

i am not able retrieve array elements one at a time if i call them in my component all the elements are retrieved at a time

I want to load my array element when an event is occurred by referencing the key i tried different variables for the key but it would not accept all the elements of the array are being displayed if i give index as the key.
I am new to Reactjs and not very familiar with all the syntax and concept can somebody help me with the logic to solve this.
The event I am triggering is onClick or onChange.
`var Qstn =['A','B','C','D','E','F','G','H','I','J'];
<div>
{Qstn.map(function(Q,index){
return <span className="col-md-4 txt" key={index}>{Q}</span>
})}
</div>`
Ok I made a codepen with an example
http://codepen.io/lucaskatayama/pen/QGGwKR
It's using ES6 classes components, but it's easy to translate.
You need to set initial state to an empty array like [].
On click button, it call onClick() method which uses this.setState({}) to change component state.
When React notice state changes, it re-render the component.
class Hello extends React.Component {
constructor(){
super();
//Initial State
this.state = {
Qstn : []
}
}
onClick(){
//Called on click button
// Set state to whatever you want
this.setState({Qstn : ['A','B','C','D','E','F','G','H','I','J']})
}
render(){
let Qstn = this.state.Qstn; // load state and render
return (
<div>
<button onClick={() => this.onClick()}>Click</button>
<div>
{Qstn.map(function(Q,index){
return <span className="col-md-4 txt" key={index}>{Q}</span>
})}
</div>
</div>
)
}
}
ReactDOM.render(<Hello />, document.getElementById('container'))

Having issue rendering components with radio buttons multiple times

A bit of context first, I'm working a questionnaire and decided to start learning React.js. Each step of the questionnaire includes a question and a yes/no radio button group, and also navigation buttons. Here's the jsfiddle. The question component is pretty straight forward, I'm passing the formData and save data function to it in the parent component.
And code for the question component
var Question = React.createClass({
getInitialState: function() {
return {
}
},
createMarkup: function(html) {
return {
__html: html
};
},
render: function() {
var subtitle = this.props.subtitle || '';
var name = this.props.name;
if (this.props.formData[name] === undefined) {
var answer_yes = null;
var answer_no = null;
} else {
answer_yes = this.props.formData[name] == "yes" ? true : null;
answer_no = this.props.formData[name] == "no" ? true : null;
}
console.log(answer_yes);
console.log(answer_no);
return (
<div className="container-question">
<label className="default" dangerouslySetInnerHTML={this.createMarkup(this.props.question)}></label>
<span className="alert subtitle" dangerouslySetInnerHTML={this.createMarkup(subtitle)}></span>
<div className="form-group form-group-radio">
<div className="row">
<div className="col-xs-4">
<input type="radio" id={name + "_yes"} name={name} value="yes" defaultChecked={answer_yes} onChange={this.props.saveData}/><label htmlFor="yes">Yes</label><br></br>
<input type="radio" id={name + "_no"} name={name} value="no" defaultChecked={answer_no} onChange={this.props.saveData}/><label htmlFor="no">No</label>
</div>
</div>
</div>
</div>
);
}
});
The problem is that when you select your answer on the first question and click on next, the radio control on the second question change with it, and same thing happens when you answer second question first. I'm storing the boolean variable for defaultChecked as local variables in the render function and logging them in the console.
Is there any obvious mistake i'm making? any help is appreciated.
Here is an updated jsfiddle that works: https://jsfiddle.net/69z2wepo/8365/
The issue is that you're using what is a called an 'uncontrolled component' in React. See this page for more details: https://facebook.github.io/react/docs/forms.html
Changing to a controlled component for your scenario means using checked instead of defaultChecked. defaultChecked is meant for uncontrolled components where the values of the input controls (in your case radio buttons) are not controlled by the component's props or render method. React is really designed to use controlled components where props/state/render drive exactly what the UI shows.
The first thing you'll notice when changing from using defaultChecked to checked is that you will no longer be able to change the radio state in the UI. The reason this happens is because although the state of your application is being changed in your saveData function, your component is not being re-rendered.
There are a few different approaches to handling this. The updated jsfiddle is re-rendering your application during saveData. Ideally this should be factored differently. Either the saveData should be moved out of the parent component and into a FormDataStore concept (see React Flux architecture, https://facebook.github.io/flux/docs/overview.html) or the parent component should own the formData state and call setState on itself in saveData which will then cause a re-render of that portion of the DOM tree.

Resources