Bootstrap Checkbox Not Toggling - reactjs

I am using a Bootstrap checkbox and a regular one. Why is the Bootstrap Checkbox failing to toggle, while the regular input field succeeds to toggle?
onChange = (evt) => {
const target = evt.target;
const name = target.name;
const value = target.type === 'checkbox' ? target.checked : target.value;
this.setState({
[name]: value
});
this.props.onChange({ name, value });
};
<input
name="unlimited"
type="checkbox"
checked={this.state.unlimited}
onChange={this.onChange} />
<Checkbox
name="unlimited"
onChange={this.onChange}
checked={this.state.unlimited}
>
UNLIMITED {this.state.unlimited.toString()}
</Checkbox>
EDIT
I made a bunch of mistakes when I originally posted code. Above is the corrected code.
EDIT
I am convinced this is a bug because Bootstrap JS library is interfering, see here.

You are passing to checked prop of the Checkbox a value from the parent via props. Are you sure you update the new state in the parent and passing a new prop again?
Here is an example using your code:
const { Checkbox } = ReactBootstrap;
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
unlimited: false
};
}
onChange = (evt) => {
const target = evt.target;
const name = target.name;
const value = target.type === 'checkbox' ? target.checked : target.value;
this.setState({
[name]: value
});
};
render() {
const { unlimited } = this.state;
return (
<div>
<input
name="unlimited"
type="checkbox"
checked={this.state.unlimited}
onChange={this.onChange} />
<Checkbox
name="unlimited"
onChange={this.onChange}
checked={this.state.unlimited}
>
UNLIMITED {this.state.unlimited.toString()}
</Checkbox>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-bootstrap/0.31.3/react-bootstrap.min.js"></script>
<div id="root"></div>
Edit
Any experience in overriding the bootstrap.js? it is on the global
template page of our app (PHP smarty)
I'm not a PHP expert but i guess you can conditionally render the script tag of bootstrap.js, So when you redirected to the page that use your react application just don't render the <script> tag.

you have a controlled input - see https://reactjs.org/docs/forms.html#controlled-components
in this case, the checked is bound to your parent component's prop fields={unlimited}
you tells your parent via onChange={} that there's a new name and value - it will be up to it to setState() in a way that passes a new fields.unlimited
Eg, your stateful parent can be:
class Foo extends Component {
state = {
unlimited: false
}
onChange = (name, value) => this.setState({[name]: value})
render(){
return <MyOtherComponent fields={this.state} onChange={this.onChange} />
}
}
if your component is stateful itself (looking at your handleChange), then you can convert to uncontrolled (https://reactjs.org/docs/uncontrolled-components.html) or bind to your local state if you sync it from props on mount and when it gets props.

Related

What are the differences between defaultValue and value in select?

I have two react components, parent and child. I'm passing a prop to a child component, and I want to use that prop to set the defaultValue on a select input. However, if that property changes, I'd like for the select default value to change as well.
When I set default value in select, I can choose one of the options that is a part of that selector. If I use value instead, the 'default' changes as the property updates, but I can't select any of the options.
class Selector extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<select defaultValue={this.props.value}>
<option>1</option>
<option>2</option>
</select>
)
}
}
I'd like for the value to change, and I realize that it is not re rendering even though the prop has changed. I'm looking for a work around.
I'm quoting:
The difference between the defaultValue and value property, is that
defaultValue contains the default value, while value contains the
current value after some changes have been made. If there are no
changes, defaultValue and value is the same.
The defaultValue property is useful when you want to find out whether
the contents of a text field have been changed.
What that actually means is that if you put defaultValue, this value will be initialized to the input and that's it, you can change value and the text will change.
But if you put value, you would need to change that value given to the input in the first place in order for input text to change.
Look at this example, all using the same state, but behaving differently.
// Example class component
class Thingy extends React.Component {
constructor(props) {
super(props);
this.state = { value: 'test' }
}
onChange(e) {
this.setState({ value: e.target.value });
}
render() {
return (
<div>
<div><b>default value</b> (you can edit without changing this.state.value)</div>
<input defaultValue={this.state.value}></input>
<div><b>value</b> (you can't edit because it does not change this.state.value)</div>
<input value={this.state.value}></input>
<div><b>value</b> (you can edit because it has onChange method attached that changes this.state.value) <br /> <b>NOTE:</b> this will also change second input since it has attached the same state with <b>value</b> property, but won't change first input becase same state was attached as <b>defaultValue</b></div>
<input value={this.state.value} onChange={e => this.onChange(e)}></input>
</div>
);
}
}
// Render it
ReactDOM.render(
<Thingy />,
document.body
);
div > div {
font-size: 16px;
}
input + div {
margin-top: 20px;
}
<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>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
When you don't have onChange handler you need to put your value as defaultValue, but in value when you have onChange handler.
You can do this,
class Selector extends React.Component {
constructor(props) {
super(props);
this.state = {
selected: props.value
}
}
change = (event) =>{
this.setState({selected: event.target.value});
}
componentDidUpdate(prevProps, prevState) {
if (prevState.selected !== this.props.value) {
this.setState({selected: this.props.value})
}
}
render() {
return (
<select value={this.state.selected} onChange={this.change}>
<option>1</option>
<option>2</option>
</select>
)
}
}
defaultValue is selected value while very first time loading
and
value is selected value every time to change option value

Click to Enable TextField React

I am displaying tabular data and each cell displays data using a TextField of Material UI (like input field). I want to show all of these TextFields as disabled at first, and enable any of them if they are clicked on. So user would click on the TextField and field would become available to alter. How can I do that?
Setting the state for field
state = {
button: false,
}
I have the TextField like below:
<TextField
disabled={this.state.button}
onClick={this.fieldActivate}
name="abc"
Activating field
fieldActivate(event) {
this.setState({
button: true
})
}
onClick and disabled
They don"t work together since disabled elements are not clickable. However, you could use something like onMouseOver.
Callback and Scope
If you want to define a function for an Event which uses this keyword, you need to either bind this or call the function from an anonymous function.
Finding the target
Since you only want one field to be enabled, you need to identify it somehow. Give them keys/ids.
Example
class MyComponent extends React.Component {
state = {
enabled: -1
}
handleMouseOver(id) {
this.setState({
enabled: id
});
}
render() {
let inputs = [];
for (let i=0; i<=20; i++) {
inputs.push({ id: i, placeholder: 'Input ' + i });
}
return (
<div>
{inputs.map((input) => {
return(
<input
disabled={this.state.enabled !== input.id}
type='text'
placeholder={input.placeholder}
onMouseOver={(e) => {
this.handleMouseOver(input.id);
}}
/>
);
})}
</div>
);
}
}
ReactDOM.render(
<MyComponent />,
document.getElementById('app')
);
input:disabled{
background: #ccc;
}
<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>
state = { currentFocusElement: '' }
...
<TextField
disabled={this.state.currentFocusElement !== 'name'}
onFocus={() => this.setState({ currentFocusElement: 'name' })}
The idea is force a re-render when onfocus, and change the disabled value

React form input won't let me change value

I have a component in a React class in my Laravel project which is a simple form with one input field. It houses a phone number which I have retrieved from the database and passed back through the reducer and into the component as a prop. Using this, I have passed it through to the module as a prop which then populates the field with the currently saved value:
<OutOfOfficeContactNumberForm
show={props.showOutOfOffice}
value={props.outOfOfficeNumber}
handleChange={console.log("changed")}
/>
I have a handleChange on here which is supposed to fire a console log, but it only ever displays on page load. Here is my form module class:
class OutOfOfficeContactNumberForm extends React.Component {
render() {
const { show, value, handleChange } = this.props;
if(!show) return null;
return (
<div>
<p>
Please supply an Out of Office contact number to continue.
</p>
<InputGroup layout="inline">
<Label layout="inline" required={true}>Out of Office Contact Number</Label>
<Input onChange={handleChange} value={value} layout="inline" id="out-of-office-number" name="out_of_office_contact_number" />
</InputGroup>
</div>
);
}
}
export default (CSSModules(OutOfOfficeContactNumberForm, style));
The form is embedded in my parent component, as follows:
return (
<SectionCategoriesSettingsForm
isSubmitting={this.state.isSubmitting}
page={this.props.page}
show={this.props.show}
categories={this.props.categories}
submitSectionCategoriesSettings={this._submit.bind(this, 'add')}
updateSelectedCategories={this._updateSelectedCategories.bind(this)}
selectedCategoryIds={this.state.selectedCategoryIds}
storedUserCategories={this.props.selectedCategories}
outOfOfficeNumber={this.state.outOfOfficeNumber}
onUpdateContactNumber={this._updateContactNumber.bind(this)}
/>
);
In my componentWillReceiveProps() function, I set the state as follows:
if (nextProps.selectedCategories && nextProps.selectedCategories.length > 0) {
this.setState({
outOfOfficeNumber: nextProps.outOfOfficeNumber,
selectedCategoryIds: nextProps.selectedCategories.map(c => c.id)
});
}
I'm pretty sure the reason it's not changing is because it's pre-loaded from the state which doesn't change - but if I cannot edit the field how can I get it to register a change?
EDIT: Just to clarify there are also checkboxes in this form for the user to change their preferences, and the data retrieved for them is set the same way but I am able to check and uncheck those no problem
Changes:
1- onChange expect a function and you are assigning a value that's why, put the console statement inside a function and pass that function toOutOfOfficeContactNumberForm component , like this:
handleChange={() => console.log("changed")}
2- You are using controlled component (using the value property), so you need to update the value inside onChange function otherwise it will not allow you to change means input values will not be not reflect in ui.
Check example:
class App extends React.Component {
state = {
input1: '',
input2: '',
}
onChange = (e) => this.setState({ input2: e.target.value })
render() {
return(
<div>
Without updating value inside onChange
<input value={this.state.input1} onChange={console.log('value')} />
<br />
Updating value in onChange
<input value={this.state.input2} onChange={this.onChange} />
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='app' />
I think the best way is when you get data from database put it to state and pass the state to input and remember if you want to see input changes in typing, use a function to handle the change and that function should change state value.
class payloadcontainer extends Component {
constructor(props) {
super(props)
this.state = {
number:1
}
}
render() {
return (
<div>
<input value={this.state.number} onChange={(e)=>this.setState({number:e.target.value})}></input>
<button onClick={()=>this.props.buyCake(this.state.number)}><h3>buy {this.state.number} cake </h3></button>
</div>
)
}
}

how react programmatically focus input

I'm trying to implement a very simple use case, a UI feature, where:
There is a label with some content in it
If clicked, a text input replaces it with the content of label available
User can edit the content
When enter is pressed, the input hides and label is back with updated content
I could get finally all correct (in fact with a MongoBD backend, redux, etc.), and the only thing I couldn't ever do (paying a complete day in googling and reading S.O.F similar posts) was this:
When my text input appears, I can't transfer focus to it! First I tired this way:
<div className={((this.state.toggleWordEdit) ? '' : 'hidden')}>
<input id={this.props.word._id} className="form-control"
ref="updateTheWord"
defaultValue={this.state.word}
onChange={this.handleChange}
onKeyPress={this.handleSubmit}
autoFocus={this.state.toggleWordEdit}/></div>
<div className={((this.state.toggleWordEdit) ? 'hidden' : '')}>
<h3 onClick={this.updateWord}>
{this.state.word}</h3>
</div>
but autoFocus sure didn't work (I "guess" because the form is rendered, but in hidden state, making autoFocus useless).
Next I tried in my this.updateWor, many of suggestions I found on google and S.O.F.:
this.refs.updateTheWord.focus();
which together with similar suggestions all didn't work. Also I tried to fool React just to see if at all I can do something! I used real DOM:
const x = document.getElementById(this.props.word._id);
x.focus();
and it didn't work either. One thing I even could not understand to put into word is a suggestion like this:
having ref as a method (I "guess")
I didn't even try it because I have multiples of these components and I need ref to further get value of, per component, and I couldn't imagine if my ref is not named, how I could get the value of!
So could you please give an idea, helping me to understand that in case I'm not using a Form (because I need a single input box replacing a label) how I could set its focus when it's CSS (Bootstrap) class is losing 'hidden' please?
The way you have used refs is not the most preferred way or else its not the best practice anymore . try some thing like this
class MyClass extends React.Component {
constructor(props) {
super(props);
this.focus = this.focus.bind(this);
}
focus() {
this.textInput.current.focus();
}
render() {
return (
<div>
<input
type="text"
ref={(input) => { this.textInput = input; }} />
<input
type="button"
value="Set Focus"
onClick={this.focus}
/>
</div>
);
}
}
Update
From React 16.3 upwards you can use the React.createRef() API
class MyClass extends React.Component {
constructor(props) {
super(props);
// create a ref to store the textInput DOM element
this.textInput = React.createRef();
this.focus = this.focus.bind(this);
}
focus() {
// Explicitly focus the text input using the raw DOM API
// Note: we're accessing "current" to get the DOM node
this.textInput.current.focus();
}
render() {
// tell React that we want to associate the <input> ref
// with the `textInput` that we created in the constructor
return (
<div>
<input
type="text"
ref={this.textInput} />
<input
type="button"
value="Set Focus"
onClick={this.focus}
/>
</div>
);
}
}
From React 18.xx upwards you can use the useRef Hook
import React, { useRef } from "react";
export const Form = () => {
const inputRef = useRef(null);
const focus = () => {
inputRef.current.focus();
};
return (
<div>
<input type="text" ref={inputRef} />
<input type="button" value="Set Focus" onClick={focus} />
</div>
);
};
Just add autofocus attribute to the input. (of course in JSX it is autoFocus)
<input autoFocus ...
useFocus hook
// General Focus Hook
const useFocus = (initialFocus = false, id = "") => {
const [focus, setFocus] = useState(initialFocus)
const setFocusWithTrueDefault = (param) => setFocus(isBoolean(param)? param : true)
return ([
setFocusWithTrueDefault, {
autoFocus: focus,
key: `${id}${focus}`,
onFocus: () => setFocus(true),
onBlur: () => setFocus(false),
},
])
}
const FocusDemo = () => {
const [labelStr, setLabelStr] = useState("Your initial Value")
const [setFocus, focusProps] = useFocus(true)
return (
<> {/* React.Fragment */}
<input
onChange={(e)=> setLabelStr(e.target.value)}
value={labelStr}
{...focusProps}
/>
<h3 onClick={setFocus}>{labelStr}</h3>
</>
)
}
For a more complete demo click here.
In addition to the previous answers, I've added setTimeout to make it work
handleClick() {
if (this.searchInput) {
setTimeout(() => {
this.searchInput.focus();
}, 100);
}
}
where searchInput is the jsx ref of the input
<input
type="text"
name="searchText"
ref={(input) => { this.searchInput = input; }}
placeholder="Search" />
and the handleClick() is an onClick handler to any element
#BenCarp's answer in typescript
Pass the inputRef to an input and just call setFocus to set the focus to it.
export const useInputFocus = (): [MutableRefObject<HTMLInputElement | undefined>, () => void] => {
const inputRef = useRef<HTMLInputElement>();
const setFocus = (): void => {
const currentEl = inputRef.current;
if (currentEl) {
currentEl.focus();
}
};
return [inputRef, setFocus];
};
Use componentDidUpdate method to every time update the component
componentDidUpdate(prevProps, prevState) {
this.input.focus();
}
You can use "useRef" hook and make a reference to your input control, then use your reference.current.focus()

How do I reset the defaultValue for a React input

I have a set of React input elements that have a defaultValue set. The values are updated with an onBlur event.
I also have another action on the page that updates all values in these input elements. Is there a way to force react to render the new defaulValues when this happens?
I can't easily use onChange since it would trigger a premature rerender (The inputs contain a display order value and a premature rerender would move them).
I could create a duplicate state, one for the real values that is only updated with onBlur and one to update the value in the input element while it is being edited. This would be far from ideal. It would be so much simpler to just reset the default values.
As mentioned in https://stackoverflow.com/a/21750576/275501, you can assign a key to the outer element of your rendered component, controlled by state. This means you have a "switch" to completely reset the component because React considers a new key to indicate an entirely new element.
e.g.
class MyComponent extends React.Component {
constructor() {
super();
this.state = {
key: Date.now(),
counter: 0
};
}
updateCounter() {
this.setState( { counter: this.state.counter + 1 } );
}
updateCounterAndReset() {
this.updateCounter();
this.setState( { key: Date.now() } );
}
render() { return (
<div key={this.state.key}>
<p>
Input with default value:
<input type="text" defaultValue={Date.now()} />
</p>
<p>
Counter: {this.state.counter}
<button onClick={this.updateCounter.bind( this )}>Update counter</button>
<button onClick={this.updateCounterAndReset.bind( this )}>Update counter AND reset component</button>
</p>
</div>
); }
}
ReactDOM.render( <MyComponent />, document.querySelector( "#container" ) );
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container" />
I've solved this by using both onBlur and onChange and only keeping track of the currently active input element in the state.
If there is a way to reset the module so that it re-displays the new default values then I'll mark that as correct.
state = {
inFocusIndex: null,
inFocusDisplayOrder: 0,
};
onOrderBlur() {
const productRow = this.props.products[this.state.inFocusIndex];
const oldDisplayORder = productRow.displayOrder;
// This can change all the display order values in the products array
this.props.updateDisplayOrder(
this.props.groupId,
productRow.productGroupLinkId,
oldDisplayORder,
this.state.inFocusDisplayOrder
);
this.setState({ inFocusIndex: null });
}
onOrderChanged(index, event) {
this.setState({
inFocusIndex: index,
inFocusDisplayOrder: event.target.value,
});
}
In the render function:
{this.props.products.map((row, index) => {
return (
<input
type="text"
value={index === this.state.inFocusIndex ? this.state.inFocusDisplayOrder : row.displayOrder}
className={styles.displayOrder}
onChange={this.onOrderChanged.bind(this, index)}
onBlur={this.onOrderBlur.bind(this)} />
);
})}

Resources