How to properly init jQuery module on React component? - reactjs

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.

Related

ReactJS is it normal that class rendering twice while I set the state

I was doing autocomplete class so I wanted to set the state in each changes of input value.
like:
state = {
value: ""
}
render(){
console.log("called");
return(
<div>
<input type="text" onChange={(e) => this.setState({value: e.target.value})/>
</div>
);
}
So in console I get "called" twice in each changes.
Could you please tell if it is fine or not?
You shouldn't assign the values to the state that way, the recommended way for Class based components is to use
this.setState()

React form + localstorage won't update values

Currently I am working on a form with the use of react hook forms and I want to save input from a user in the localstorage. At this moment whenever I change the value of the input when loaded from localstorage the input wont change.
When I console.log the event from the onchange method I don't get a log so it didn't trigger the onchange and thats where my problem is. I want that when a user wants to change anything in the form that it should update again.
I have searched on google but I can't find any related problem and so no solution. Since I am a beginner in react I have no clue to solve it myself.
The onchange function is in a functional component same as the input components is only functional.
This is the onchange function that contains the input event.
const onChange = event => {
localStorage.setItem(event.target.id, event.target.value);
};
This is the input compontent
<Input key={index} field={item} formFunction={register({required:true})} checkChange={handleChange} onChange={e => onChange(e)} value={localStorage.getItem(item.label) || ''} errors={errors} selectCheck={handleCountryChange} touched={touched} />
And this is the input compontents code
return (
<div className={props.field.grid}>
<div className={props.field.inputClass}>
<input
type={props.field.type}
name={props.field.name}
className={props.field.class}
id={props.field.label}
data-save="persist"
onBlur={props.checkChange}
style={{ marginBottom: '5px' }}
onChange={props.onChange}
value={props.value}
/>
<label className="left" htmlFor={props.field.label}>{props.field.label}</label>
</div>
</div>
);
Your problem is you are using the local storage to try and update the state of the app so the render function will not get re called and display the new inputted. ( unless you can show more code and you are indeed updating that this.props.value )?
I would suggest looking up local state within component for react, it will make things 10x easier in the future:
React state
Functional component state
You are best creating a local state in your constructor if it is an class component e.g., same can be achieved if it is a functional component just slightly different.
this.state = {
inputVariable: ""
}
then when ever your change this variable(in your onchange function using set state):
setstate({
inputVariable: valueToUpdate
})
your input components value field should be populated with this.state.inputVariable, so as you change the value it will trigger on change and then update the state which will cause a re render of your UI.
if you additionally also to save it to local storage you can do so like you already have.

React warning uncontrolled component in child component

I have child form and input components that are rendered within a parent project component. In the parent component, they are "uncontrolled" as in their value isn't initially set by the parent project's state. Their value is set by the form component state immediately. React keeps giving me this "Warning" (which registers as an error in the console).
Warning: `value` prop on `input` should not be null. Consider using an empty string to clear the component or `undefined` for uncontrolled components.
I understand the issue with controlled vs non controlled components. But how do I get React to see that the child is controlling the input rather than the parent and stop giving me this error?
Other relevant info: this is an external forms module that I created because my apps use a lot of forms and I wanted a standardized way to implement this with just an "npm install ...". This is the sudo code
<Parent project module>
//external forms components
<Form>
<Input/>
<Input/>
<Button/>
</Form>
//end external forms components
</Parent project module>
Here is the render method in the Form component that allows me to control the Input state from withing the Form component. Please let me know if other code snippets or other info would be helpful. Am I going about this incorrectly or is there some way to get rid of these errors? Everything works fine, but it clutters my console and makes it difficult to code.
render() {
const inputs = React.Children.map(this.props.children, child =>
React.cloneElement(child, {
value: this.state.value,
onChange: this.onChange,
error: this.state.userNotify[child.props.name]
})
);
return (
<div id="form-container">
<p className="formTitle">{this.props.formTitle}</p>
<form id={this.props.formID} onSubmit={this.onSubmit} >
{inputs} {/*there must be nested input components passed in*/}
</form>
</div>
)
};
You could try assigning a fallback value to your input components when their state value is not set yet.
const inputs = React.Children.map(this.props.children, child =>
React.cloneElement(child, {
value: this.state.value || '',
onChange: this.onChange,
error: this.state.userNotify[child.props.name]
})
);
This ensures that the value will never be undefined or null thus removing those console warnings.
You must be having value intialized in state
Like in constructor
this.state = {
value: null
}
Or on class level
state = {
value: null
}
So change value: null to value: ""
Thank you both for your suggestions. While they were not solutions in this case, they helped me to think through and identify the problem.
I have a prop on the Input component called prepPopVal. I use it to pre-populate the input with some value. For example; when viewing existing data, you can use my Form/Input component to display that data. It looks like this in the parent project
<Parent project module>
//external forms components
<Form>
<Input prePopVal={this.state.someValue}/>
<Button/>
</Form>
//end external forms components
</Parent project module>
I was using a conditional like this
if(typeof this.props.prePopVal === 'undefined')
var value = this.props.value;
else
var value = this.props.prePopVal;
then placing the "value" variable as the value of the html input
<input className="textinput"
type={type}
id={this.props.name}
name={this.props.name}
value={value}
onChange={this.props.onChange}
autoComplete="off"
/>
What I should have done is eliminated the conditional logic to set "value" and just used the "||" operator within the input to decide which to use, like this:
<input className="textinput"
type={type}
id={this.props.name}
name={this.props.name}
value={this.props.prePopVal || this.props.value}
onChange={this.props.onChange}
autoComplete="off"
/>
This way if "prePopVal" isn't defined it uses "value". This cleared my console error.
Thanks again. I hope this question is useful to someone.

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