Ace Editor clearing on react render - reactjs

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')
);

Related

React Radio input selection persists on re-render

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.

Updating react state in parent very slow

I'm developing a react application, and where I always thought the react state updates were really fast, I now found a problem.
I have a page view with a lot of elements on it, one of the elements is this one that gets loaded in the page:
<NotesCard notes={deal.notes} updateNotes={notes => {setDeal(prevState => ({...prevState, notes}))}} />
NotesCard is a child component that only renders a material-ui Card with another react component in:
export default function NotesCard(props) {
const {notes, updateNotes} = props;
return (
<Card className="Card">
<CardHeader
title="Notities"
/>
<CardContent>
<Notes notes={notes} onChange={updateNotes} />
</CardContent>
</Card>
);
}
Notes is the last component that renders a text field and just takes the props to the TextField:
function Notes(props) {
const {notes} = props;
function updateNotes(event) {
// props.deal.notes = event.target.value;
props.onChange(event.target.value);
}
return (
<div>
<FormGroup>
{notes !== null ?
<TextField
multiline
defaultValue={notes}
onChange={e => updateNotes(e)}
rows={3}
variant={'outlined'}
label={'Notities'}
/>
: 'Geen beschrijving...'}
</FormGroup>
</div>
);
}
Is there anything that I do wrong that creates a lot of lag? The page is a big page so it might have something to do with that, but I'd think that the updates performances would still be okay.
Your goal is to fix the slow render, only then you will want to take a look at number of rerenders if necessary.
Please install the react-dev-tools which contains an option to mark components when the are being rerendered. Alternativly you can also monitor the performance over a couple of seconds and investigate the rendering. This should help you understand what renders unnecessarily on your actions.
I see a potential problem with this one:
<Notes notes={notes} onChange={updateNotes} />
If you trigger onChange the parent state is mutated. This then causes ALL children to rerender. I would think that only the single will change and a change in this component wont effect other siblings. So try to move the state as close to where its used as possible. If you trigger onChange only the should be updated. This is something easy which can fix a ton of performance problems without using react features like Memo.

How to properly init jQuery module on React component?

I have parent React component called Sorter. Inside of Sorter I have child component for range slider:
<div className="child">
<rangeSlider props=... />
</div>
<rangeSlider props={...}/> returns simple input:
render() {
return <input type="text" id={this.props.id} name={this.props.id} value=''/>
}
And then I call module initialization via componentDidMount():
componentDidMount() {
jQuery('#' + this.props.id).ionRangeSlider(config)
}
And everything works fine until I actually use range slider. I have callback in config, which updates parent (in this case it's Sorter's) state. After that range slider just disappears. I tried to reinit it via componentDidUpdate() but it didn't do the trick.
Am I doing something wrong?
I have tried this in codesandbox:
https://codesandbox.io/s/summer-sea-imwpp?fontsize=14
As you can see, it's working fine.
Could you please provide the error message or more details?
Such as :
Is the RangeSlider has to update when the range changed?
How does Sorter change its state with the ionRangeSlider config?
Thanks.
I figured it out. Since I call <RangeSlider props={...} /> render with new props everytime, it triggers React to update DOM and to run re-render.
From the very beginning we have rendered only a single <input> element which has only static attributes that will never change:
<input type="text" id={this.props.id} name={this.props.id} value='' readOnly />
And ionRangeSlider, when called, prepends it's own DOM elements to this input and after this happens we don't need to interact with this input anyhow.
Thus we don't have to re-render it everytime new props arrives. So I just wrapped input in empty <div /> and prevented render from being executed:
shouldComponentUpdate() {
return false
}
render() {
return (
<div>
<input type="text" id={this.props.id} name={this.props.id} value='' readOnly />
</div>
)
}
Yet still I don't think it's a good practice to write dummy methods like this that only returns single static value, but as long as my rendered DOM won't change under no circumstances the situation is under control.

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>
);
}
}

React access Dom Nodes from this.props.children

Let's say I have a Card that contains a login Form
<Card>
<LoginForm/>
</Card>
How do I access the nodes from the Form within the Card render function?
<Form >
<input type="text" name="email"/>
<input type="password" name="password"/>
<input type="submit"/>
</Form>
Because what i´d like to do is to render the submitbutton not within the props.children context but render it wrapped outside of the given child!
render () {
return (
<div className="card">
<div className="inner">
{/* render Children */}
{this.props.children != undefined ?
<div className="childrenWrapper">
{this.props.children}
</div>
: ""
}
</div>
{/* render submit from login form here, not above */
</div>)
There are some components which actually do what I want. For example the Tabs component from react-toolbox. They somehow manage to render what's within the Tab (children) somewhere else
Just for instance
<Tabs index={this.state.inverseIndex} onChange={this.handleInverseTabChange} inverse>
<Tab label='First'><small>First Content</small></Tab>
<Tab label='Second'><small>Second Content</small></Tab>
<Tab label='Third'><small>Third Content</small></Tab>
<Tab label='Disabled' disabled><small>Disabled Content</small></Tab>
</Tabs>
Which will lead to the following html
As you can see the children from the tab where rendered within their own section
I do not want to change anything on the Form to solve this problem, I would like to pass the Form into the Card and let the Card decide how the Form will be rendered within the card render function.
Since I'm trying to implement the Google Material Design Card component and just use it as a template there are more elements coming which will need to be split up and placed at the positions I want them to be. The thing is I could actually place the relevant HTML around the Form to get it as the Card I want it to be, but then I wouldn't need the component at all.
There are some decent answers here, but none of them directly answer your question. Therefore, even though you should refactor your code (as elucidated below), I am going to provide you a working solution:
class Card extends React.Component {
constructor() {
super();
this.state = {};
}
render() {
console.log(typeof this.props.children)
return (
<div>
{typeof this.props.children === 'object'
? React.cloneElement(this.props.children, { ref: (n) => this.form = n })
: null}
<button onClick={(e) => console.log(this.form.data)}>submit</button>
</div>
);
}
}
class Form extends React.Component {
constructor() {
super();
this.onChange = this.onChange.bind(this);
this.state = {};
}
onChange(e) {
this.data = e.target.value;
}
render() {
return (
<form>
<input type="text" onChange={this.onChange} />
</form>
);
}
}
ReactDOM.render(
<Card><Form /></Card>,
document.getElementById('container')
);
https://jsbin.com/fohehogozo/edit?js,console,output
By setting a property on the instance, you can then access that property from children by using a ref. I checked for typeof === object here, because there was only one child.
WARNING: this code is NOT PRODUCTION READY. Do not ever run this in production. The code I have demonstrated is a terrible hack, and you should never try this at home.
If you are trying to submit a form, maybe look at passing down an onChange event and storing the value (based on the name of the field) in the state of the Card. Then attach the onChange event on the inputs so as soon as they're updated, the data will be passed back up to the container for you to submit.
If you would like to split up the childrens passed, you can simply filter the children array to split up the children, however your childrens seem to be nested.
Why dont you let the cards children handle the separation between your inner container and other content?
I think restructuring in this case is more suitable than modifying the passed children property.
Also, pulling the submit button out of the actual form tags, would break your form as it would no longer submit without some custom connection between the button and the actual form.
Don't try to manipulate the DOM; it's generally an anti-pattern in React (though there are a few valid use cases). In your case, rather than literally trying to move the elements, I'd simply hide the button in the form and add it to the parent.
Assuming you have access to the internals of <LoginForm>, you can add a prop to hide the button:
const button =
<div class="flatbuttonWrapper">
<input type="submit"/>
</div>;
<Form>
<input type="text" name="email"/>
<input type="password" name="password"/>
{!this.props.hideButton && button}
</Form>
Add the button to the Card component:
render() {
return (
<div className="card">
<div className="inner">
{this.props.children != undefined ?
<div className="childrenWrapper">
{this.props.children}
</div>
: ""
}
</div>
<div class="flatbuttonWrapper">
<input type="submit"/>
</div>
</div>
);
}
Finally, in your parent:
<Card>
<LoginForm hideButton />
</Card>
All that said, it really feels like you need to structure your code better and break some of these components up into smaller, more reusable pieces. For example, the Card component probably shouldn't be affecting the button's style or conditionally rendering children; it should just add a frame around any children. Then you can create a more complex component that composes these simpler sub-components to to whatever you need.

Resources