I'm trying to figure out why a call to this.setState isn't updating state.
I have the following lines method:
changeState(state, value){
this.setState({state:value}, ()=>console.log(this.state));
}
There are a few function using this, but one is a file upload component. Its a simple component that either renders a file input OR it renders an iframe, disabled input, and button. When the button is clicked I want to change the state in the parent component which then rerenders the file component and shows a file picker.
The call to this.setState seems to work ok, but when I console log state after it runs or if I stop execution before the next render takes place state is unaffected. My file upload component has a method like this:
renderField(field){
if(this.props.hasOwnProperty('file') && this.props.file){
return(
<div>
<iframe src={this.props.file} frameborder="0"> </iframe>
<input
disabled
placeholder={this.props.file}
name={field.name}
type='text'
/>
<span onClick={()=>this.props.changeFile(this.props.file_type, null)}>× Remove</span>
</div>
)
}else{
return(
<input
{...field}
type={field.type}
name={field.name}
onChange={(event) =>{
field.input.onChange(field.input.value = event.target.files[0])}
}
/>
)
}
}
when I call the method I get this output:
however after console logging my state is anything but changed:
You don't want to set the state property in your state, but the property name that state contains.
You can use a computed property name for this.
changeState(state, value) {
this.setState({ [state]: value }, () => console.log(this.state));
}
Related
As you can see the code from my Visual Studio Code, it won't let me type within the text input if I render the code
The onChange event on line 36 invokes (updateInput) function.
Which doesn't exist or declared inside the component, if you sent it as a prop use :
this.props.updateInput(_params_)
Otherwise declare the function inside the component in order to make the event work properly.
That's because <input /> has constant value, so you need to change value to defaultValue.
<input
type="text"
placeholder="Insert text"
defaultValue={this.state.newItem}
onChange={() => {}}
/>
I had a form component with the following content
function Form() {
return (
<div className="form-container">
<div className="form-control">
<label id="text-input">Text </label>
<input type="text"></input>
</div>
<div className="form-control">
<label>Time </label>
<input type="text"></input>
</div>
<div className="form-control" style={{alignItems:"center"}}>
<button className="add-but">Add</button>
</div>
</div>
)
}
I wanted to focus the text element after the component gets rendered.
I was first trying to put {document.querySelector("#txt-in").focus()}, it didn't work and after searching I found I could use the tag autoFocus. and everything would work beautifully.
But I was wondering, what if I want to actually execute javascript code after rendering? or actually do this in javascript? I found answers for class based components but couldn't find for function based components like the one I am using.
how do I execute code I want executed after the element is rendred, in function based components?
You can use React Hooks useEffect for your purpose.
Simply put below code
import React, {useEffect} from "react";
function Form() {
useEffect(() => {
// Do whatever you want after first render
// Put your code here
}, [])
}
Similar to what the components are writing, previously one would use functions likecomponentDidUpdate & componentDidMount to manipulate components after/before being rendered. Funcitonal components realised we could use one 'hook' for this called useEffect where one can trigger a particular action to occur on the basis of a state change to the component.
Here is a link to the docs for useEffect - https://reactjs.org/docs/hooks-effect.html
on the homepage of React, there's the last example (A Component Using External Plugins) with a textarea:
<textarea
id="markdown-content"
onChange={this.handleChange}
defaultValue={this.state.value}
/>
As I type, the textarea gets updated.
Now, I tried to change defaultValue with value:
<textarea
id="markdown-content"
onChange={this.handleChange}
value={this.state.value}
/>
And the outcome is the same (as with defaultValue, i.e. as I type, the textarea gets updated visually with the updated text).
So, what is the real difference between the two?
I think a good example for this is if you use a hard coded string
using defaultValue prop:
function App(){
return(
<textarea
defaultValue="foo" // only by default foo
/>
);
}
ReactDOM.render(<App/>, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
using value prop
function App(){
return(
<textarea
value="foo" // will forever be foo
/>
);
}
ReactDOM.render(<App/>, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
So while the snippet below this paragraph might look like it is the same as using value prop because of onChange potentially updating the state value (therefore it looks like it is updating defaultValue prop) - it is not. It is still an uncontrolled component and will update its value directly from the user input. It is simply initialized with the default value of whatever this.state.value is when it is initially rendered.
<textarea
id="markdown-content"
onChange={this.handleChange}
defaultValue={this.state.value}
/>
As long as you change the value that is used in value there won't be any difference. If you won't update the variable and have set a textareas value you can't change the value of the textarea by typing. By using a defaultValue you don't have to update any variable.
see demo image here
You can edit the input value without onchange event handler when you use default value with the input tag.
If you use value with input tag you need to use onchange event handler to make changes to input value.
I have something like this in my parent scope:
<form-error :errors="errors"></form-error>
<textarea class="form-control" name="post" cols="30" rows="8" #keydown="errors.clear('post')" v-model="post"></textarea>
Note the #keydown event, where I am clearing out the errors, by calling
method on a class.
With :errors="errors" I am passing instance of the below Errors class,
into <form-error> child component:
class Errors {
constructor() {
this.errors = {};
}
get(field) {
if (this.errors[field]) {
return this.errors[field][0];
}
}
clear(field) {
delete this.errors[field];
}
has(field) {
return this.errors.hasOwnProperty(field);
}
}
And in <form-error> child component I have this:
<template>
<div v-if="errors.has('post')" class="alert alert-danger" v-text="errors.get('post')"></div>
</template>
<script>
export default {
props: ['errors']
};
</script>
Now, while v-text="errors.get('post')" works fine, and I am getting error
displayed, the v-if="errors.has('post')" part doesn't work at all.
I am assuming errors is passed the right way as props, otherwise that
errors.get('post') wouldn't work.
Question is, why when parent triggers that #keydown event, and I see the
errors object is being emptied properly (Vue addon for chrome), the v-if
part doesn't update, thus hiding the div?
As you can see, the <FormError> child component is being updated to reflect the change in errors when I start typing, but still v-if doesn't trigger.
Edit
What's even more confusing, docs say:
Note that objects and arrays in JavaScript are passed by reference, so if the
prop is an array or object(as in my case), mutating the object or array itself inside the
child will affect parent state.
Although of course I am not mutating the object from with in my child, but the
important part is that object changes in parent should be reflected in child.
Are you getting any error. As I can see Map.has has poor support in browsers. You can try using any of following alternatives:
post in errors
<template>
<div v-if="'post' in errors" class="alert alert-danger" v-text="errors.get('post')"></div>
</template>
errors['post']
<template>
<div v-if="errors['post'] !== undefined" class="alert alert-danger" v-text="errors.get('post')"></div>
</template>
I'm creating my first application with ReactJS and I found this warning when I run my code :
Warning: Failed form propType: You provided a checked prop to a form
field without an onChange handler. This will render a read-only
field. If the field should be mutable use defaultChecked. Otherwise,
set either onChange or readOnly. Check the render method of
Login.
Can someone tell me how I fix it please ?
React has 2 ways of working with form controls - Controlled Components and Uncontrolled Components
You get this warning when you don't supply the element neither the attributes needed for controlled nor those needed for an uncontrolled component:
Warning: Failed form propType: You provided a checked prop to a form
field without an onChange handler. This will render a read-only field.
If the field should be mutable use defaultChecked. Otherwise, set
either onChange or readOnly. Check the render method of Login.
Controlled Components
Attributes needed:
value - <input> (not checkbox or radio), <select>, <textbox> or checked for (checkbox or radio).
onChange
React handles the condition of the element by updating the value or checked attribute (depending on the element) from the props or the state. We need to notify react when we make a change, like inserting data, or checking the box, so react can update the element's condition when it rerenders the component. To do so, we must include an onChange handler, in which we will update the state or notify the component's parent, so it will update the props.
<input
type="checkbox"
checked={ this.props.checked }
onChange={ this.checkboxHandler }
/>
const { render } = ReactDOM;
class Demo extends React.Component {
constructor(props) {
super(props);
this.state = {
checked: true
};
this.checkboxHandler = this.checkboxHandler.bind(this);
}
checkboxHandler(e) {
this.setState({
checked: e.target.checked
});
}
render() {
return (
<input
type="checkbox"
checked={ this.state.checked }
onChange={ this.checkboxHandler }
/>
);
}
}
render(
<Demo />,
document.getElementById('demo')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react-dom.js"></script>
<h1>The Checkbox</h1>
<div id="demo"></div>
Uncontrolled Components
Attributes needed:
defaultValue - <input> (not checkbox or radio), <select>, <textbox> or defaultChecked for (checkbox or radio).
React sets the initial value using defaultValue or defaultChecked, and the update of the element's state is controlled by the user, usually via the DOM using refs.
<input
type="checkbox"
defaultChecked={ this.props.checked }
/>
The defaultChecked may not be updated if the component is re-rendered in future so use this approach with caution.
You may be better off just using a blank function to remove the warning. Especially if you want to handle click on the whole div which includes the checkbox and the associated text.
<div onClick={this.handleClick}>
<input type="checkbox" checked={this.props.checked} onChange={()=>{}}/>
{this.props.text}
</div>
You need to add defaultChecked attribute to your checkbox:
<div>
<input type='checkbox' defaultChecked />
</div>
For those that prefer a Functional Component instead of a Class Component this Controlled Component approach is simple and easy to implement. If you don't know what a Controlled Component is then please refer to #Ori Drori's well explained answer in this thread.
import {useState} from "react";
export default function YourComponentName(){
const [checked, setChecked] = useState(true);
return (
<>
<input
type="checkbox"
checked={checked}
onChange={() => setChecked(!checked)}
/>
</>
);
};
If your confronting with this warning, You can add "readOnly" to your input. like this code:
<div>
<input type='checkbox' checked={ props.checkBoxChecked } readOnly />
</div>
Or You can add an onChange event like an empty arrow function or what kind of function you care about to your input. like this:
<div>
<input type='checkbox' checked={ props.checkBoxChecked } onChange={() => {}} />
</div>
Also you must care about the value property, too.
This solution fixed my issue with the same warning, hope to be useful.