I have a requirement wherein I need to create fields dynamically through JSON.
I have created my components as required and the page is rendered with required fields.
But the fields are in non editable state, even though I had a onchange function in InputField js file. Following is the part of the code from my component
onChange(event) {
this.setState({
[event.target.name]: event.target.value
})
}
render() {
return (
this.props.screenField.fieldType === 'INPUT'
? <div className='form-group'>
<label htmlFor={this.props.screenField.name}>{this.props.screenField.label}:</label>
<input type={this.props.screenField.dataType === 'BOOLEAN'
? 'checkbox'
: this.props.screenField.dataType}
name={this.props.screenField.name}
id={this.props.screenField.name}
placeholder={this.props.screenField.helpText}
required={this.props.screenField.isRequired}
value={this.props.screenField.value} className='form-control'
onChange={this.onChange.bind(this)}/>
</div>
: null
)
}
Please find the URL below of the entire code.
https://github.com/yvsivateja/ReactJSApplications/tree/one/React-json-render
The React philosophy is that props should be immutable and to use the state if you need to change something.
Bind the value to the state not props. In the constructor set the initial state:
constructor(props) {
super(props);
this.state = {[this.props.screenField.name] : this.props.screenField.value};
}
bind to the state:
value={this.state[this.props.screenField.name]} className='form-control'
You might want to move all the logic out of the render function as that is usually good practice. The render function should only contain the components defined in a declarative way, just like HTML.
One thing you seem to be missing is the braces that are used to evaluate an expression inside JSX. Whatever you have in the return statement right now should be enclosed in {}:
{this.props.... === 'INPUT' ? ... : ...}
As I mentioned before, think about moving that outside render. Maybe you could use an if statement before the return. Another option is extracting the div into its own component (or just return it from a helper function).
Related
I have a form that I want to prefill with values if exists, but when I use it like below with React-Select it won't select the options on select
value={rowData?.components ? getOptions(rowData.components, components, 'name') : []}
Obviously, this is the problematic part : [].
I've also tried : null but it doesn't help.
When I tried this it just didn't render the default values:
rowData?.components && getOptions(rowData.components, components, 'name')
How can I work around this?
Eventually I've used defaultValue instead:
defaultValue={rowData?.components ? getOptions(rowData.components, components, 'name') : []}
so it doesn't override the default field, but still provides what I needed. for some reason it didn't work before, but after I've updated React-Select it worked fine.
class Selector extends Component {
constructor(){
super(props);
this.state={
selectedValue:''
}
}
componentDidMount(){
/*
fetch the value and assign to state
*/
this.setState({selectedValue:fetchedValue})
}
render(
return(
<ReactSelect value={this.state.selectedValue} onChange={()=>this.setState({selectedValue:option})} />
)
)
}
The problem happening is you are fixing the value if already present and that value is not updating so a better option can be to store the value in a state and then onChange change the value of the checkbox.
So I'm quite new on web development last couple of days. I come from c++ background and I can't wrap my head through all the principles of reactjs. I have 2 classes. The child class called JobAd should render some information that it got from props.
export default class JobAd extends Component {
constructor(props) {
super(props);
this.state ={
index: props.index,
id: props.jobId,
name: props.name,
description: props.description,
location: props.location,
adress: props.adress,
alreadyApplied: props.alreadyApplied,
open: false,
// toggleJob: props.toggleJob,
};
this.toggleJob = props.toggleJob;
}
render() {
return (
<div className={`${styles.jobAd} d-flex` + "job " + (this.state.open ? 'open': '')} key={this.state.index} onClick={() => this.toggleJob(this.state.index)}>
<div className={`${styles.jobTitle}`}>
{this.state.location} - {this.state.name}
</div>
<div className={`${styles.jobDetails}`}>
<div className={`${styles.jobDescription}`}> {this.state.description}</div>
<div className={`${styles.jobAdress}`}>{this.state.adress}</div>
<ApplyButton jobId= {this.props.id} alreadyApplied = {this.props.alreadyApplied}/>
</div>
</div>
)
}
}
The second class, queries a mongoDB db and creates jobAd objects populating them from the info gotten from db.
class JobExplorer extends React.Component
{
...
result.data.jobs.forEach(job => {
var find = job.employees.find(obj => obj === userId);
if (!(find === undefined)) {
alreadyApplied = true;
}
var toPush = new JobAd ({
index: i,
id:job._id,
description:job.description,
name:job.name,
location:job.locationName,
adress:job.locationAdress,
alreadyApplied:alreadyApplied,
open:false,
toggleJob: this.toggleJob.bind(this)
});
jobList2.push(toPush);
console.log("look");
console.log(jobList2)
});
this.setState({
jobList: jobList2
})
this.setState({
error: null,
jobs: result.data.jobs
});
...
render()
{
console.log("look2");
console.log(this.state.jobList);
return (
<div><Navigation />
{this.state.jobList}
</div>
);
}
But I am faced with the following error which I cannot find a fix for.
Error: Objects are not valid as a React child (found: object with keys {props, context, refs, updater, state, toggleJob}). If you meant to render a collection of children, use an array instead.
How should I instantiate those objects so I could render them using the "architecture" I wrote. Is there a fundamental flaw that I have in my classes?
The below snippet doesn't work because new will return an object (this) not the react component.
So, instead of
var toPush = new JobAd({
index: i,
id: job._id,
...
});
jobList2.push(toPush);
you can do this
var toPush = <JobAd
index={i}
id={job._id}
...
/>;
The above snippet works because <JobAd ... /> is converted to React.createElement(JobAd, ... ). However, you still shouldn't do it like this. since there are a lot of better ways to do this. one of them is:
save just the data in joblist and then render the data list on JobAd component
like below:-
render(){
return this.state.joblist.map((job, i) => (
<JobAd
key={job._id}
index={i}
...
/>
));
}
The key is a really important thing. Read about it: https://reactjs.org/docs/lists-and-keys.html
Things that could be improved:-
Don't copy props in the state as you are doing in JobAd class instead directly render the props.
Don't call setState twice as in JobExplorer. you could set all the keys in
setState at the same time. since that would render the component twice.
Suggestions:-
You should avoid using var as that might cause some issues here.
since, you are just a starter, try using functional component first. they are
quite easier to grasp
You seem to have a misconception about state/props in React and web development. It's very normal; I learned python and Java first and many tutorials seem to assume that people just know this already.
"State" in generally refers to variables containing/referring to values that can change without a page refresh in your application. If you know a value is not going to change, it does not need to be held in state. Storing it in a normal variable is exactly what you should do.
"Props" is just another word for arguments that are passed to React components. There's more to it in reality, but as a beginner, that's all you need to really know for now.
So in your job add, things like name, address, jobs, description shouldn't go in state because they aren't going to change as a result of user interaction or for any other reason, unless the underlying data they are loaded from changes, but then that wouldn't be handled by React but instead by the API that your app gets data from. They should just be rendered, so refer to them like this.props.address in your render method. The value for open, however, need to be in state, because that definitely can change.
As for the error, it looks like you are not calling JobAd correctly. You need to use the syntax <Job Ad/> rather than new JobAd...that won't work in React.
I would recommend doing a tutorial to get the basics down.
Can someone explain me below function line by line
handleChange(e) {
let fields = this.state.fields;
fields[e.target.name] = e.target.value;
this.setState({
fields
});
as my understanding
we are creating a variable fields and storing the present state of fields in the variable because we cant mute the state.
i don't have idea about fields[e.target.name] = e.target.value;
updating the fields
can someone explain me the above function line by line?
fields[e.target.name] this syntax is also used to access the Object Properties.
var obj = {name:'abc', color:'red'}
console.log(obj.name) // this print 'abc'
console.log(obj['name']) // also print 'abc'
with obj['xxx'] syntax you can dynamically read and set properties to the object.
reference https://www.w3schools.com/js/js_objects.asp
So in your problem fields variable take existing fields object from the local state. And in each event it create new property with that event target name and add that event target value as that new property value. if that property name already exist within the fields object then it only modified the value of that property.
So as in your list,
is correct. With let fields = this.state.fields; it get previously stored values into the fields variable. without losing previous data
this is what I explained above
your answer is correct. update the state with old and new values
This code let's you dynamically change the value of a field in your component state by using object notation. object[]
By using fields[event.target.name] you're going to look for a field in your component state that matches the name of the element that's causing the event to occur. Then it looks like you're updating the value for that field with event.target.value
Why this is useful
Let's say you have a component where you want to retrieve multiple inputs from a user. Without object notation, you might end up writing very repetitive code like writing a different event handler for each input in order to determine what field to update in your component state:
BAD:
handleOnChange1 = (event) => (this.setState({userName: event.target.value}))
handleOnChange2 = (event) => (this.setState({lastName: event.target.value}))
However, by naming your elements and coordinating them with a matching field in your component state, you won't have to worry about writing additional event handlers as long as you use object notaton.
GOOD:
class UserForm extends React.Component{
state = {
firstName: "",
lastName: ""
}
handleOnChange = (event) => {
this.setState({
[event.target.name]: event.target.value
})
}
render(){
return(
<div>
<input name="firstName" value={this.state.firstName} onChange={this.handleOnChange}/>
<input name="lastName" value={this.state.lastName} onChange={this.handleOnChange}/>
</div>
)
}
}
I have an Input element that I want to display an error on when the form validation fails.
<Input ref="amount" error={false} />
When the user enters an incorrect amount, I want to change "error" to "true". How can this be done?
I have tried:
this.refs.amount.props.error = true;
Which seems bad but I'm not sure how else. If I add a conditional statement in the definition of the Input element, that seems to only evaluate once and then remain the same. Do I need to force an update on the element? If so, how?
Yes it's possible to validate the input when the form is submitted.
All you need is to keep track on input value and use same approach as #SajithDilshan for the input error.
this.state = {
error: false,
value: ''
}
...
render(){
return
...
<Input
ref="amount"
value={this.state.value}
error={this.state.error}
/>
...
}
Then onSubmit should looks like:
onSubmit(e){
const isError = this.state.value === '';
this.setState({error: isError});
// rest of your logic
}
Hope it will help!
Use the onChange() method on the input as below.
<Input ref="amount" onChange={this.onInputChange} error={this.state.error} />
After that implement the onInputChange() method as below in your component.
onInputChange = (e) => {
if (e.target.value === "") { // logic to validate the input
this.setState({error: true});
} else {
this.setState({error: false});
}
}
Note that this will add error property to the state.
Further, you should not modify the props within a component. Props are passes from parent component to the child component as immutable inputs.
This is not exactly the answer, but still:
This type of fiddling with each possible state of form element (valid, invalid, warning, show tooltip, was edited, in focus, left focus, was submitted, submit failed or not, etc) becomes to much trouble when the form grows beyond 1 input field.
I would suggest to use redux-form package that integrates with semantic-ui-react` almost perfectly and provided you have provided it with the validate function does everything else for you. It takes some time to understand the basics of it, but it really pays.
I have a form where i have to enable/disable certain DOM elements based on the state of other DOM elements. For e.g. I have a radio button, on the click of which a drop down should be enabled.
Now for implementing this, should I again follow the redux way of disposing an action when the radio is clicked and then within the reducer change the state and then enable/disable the dropdown?
Does redux-form in any way simplify this process? What is the best practice to implement this in a react-redux setup?
I use redux-form for conditional inputs. For example, I have a checkbox that when checked, should display a text area to explain the true input. That looks like this:
<div className="checkbox">
<label for="trueInput">
<input type="checkbox" {...trueInput} />
Is this input true?</label>
</div>
<div className={!trueInput.value ? 'conditional-input' : ''}>
<label for="trueInputExplanation">Why is this input true?</label>
<input className="form-control" {...trueInputExplanation} />
</div>
The class .conditional-input has styling to hide the element. I'd imagine you could do this the same way for disabled, by way of using a ternary function that returns true or false, depending on the conditions you need.
Redux Form keeps track of everything in the store. (It's easy to see what's going on with the Redux Chrome dev tool.) Say I have a master checkbox whose enablement allows me to toggle a slave checkbox. So I want to put the master state read from the form into props:
const mapStateToProps = (state) => {
const isMasterChecked = state.mySetting.isMasterChecked;
const form_mySetting = state.form.mySetting;
const form_isMasterChecked = form_mySetting ? form_mySetting.values.isMasterChecked : null;
return {
isMasterChecked,
form_isMasterChecked
}
};
and then for the form you have
const {isMasterChecked, form_isMasterChecked} = props;
const shouldDisable_slaveCheckbox= () => {
if (form_isMasterChecked == null) return isMasterChecked; // the form is not fully built yet, so use "real" store value instead of reading the form via store
return form_isMasterChecked;
};
<Field name="isSlaveChecked" component="input" type="checkbox" disabled={shouldDisable_slaveCheckbox() ? "" : "disabled"}/>
Use sparingly, as this approach may cause entire form redraw.