ReactJS - Checkbox doesn't re-render if click-handler is instance method - reactjs

I prepared a demo on JS Fiddle to demonstrate the problem.
Please, have in mind that in my real world example I have a lot of custom logic - which I've skipped here - that's why some parts of the code in the JS Fiddle (may) look strange.
===
The problem - if you click on the labels - the wrapper and the inner components get updated and rendered correctly.
However - if you click on the checkboxes themselves - then the wrapper gets updated, but the checkboxes doesn't render correctly.
https://jsfiddle.net/dbjfvsm2/5/
At the same time - if I directly call the property handler from the checkbox - then all is fine:
https://jsfiddle.net/dbjfvsm2/6/
I tried with onChange as well, but same result.
Why is this happening? ... And I really need to have that instance method - onItemClick - that is reused by both the label and the checkbox, this is where some general stuff is happening.

The only thing preventing checkbox to being checked is e.preventDefault(); in onItemClick function. Try removing it and it will work.
onItemClick = (e, id) => {
this.props.handleCheckboxClick(id);
}
Why it didn't work? Your answer is here

I've updated your fiddle in order to get your desired result
https://jsfiddle.net/sabbin/kh4j1Ltp/12/
You should not use the verification in the child component in the first place, also e.preventDefault was causing the an issue in your logic. Also avoid using arrow functions inside the render, you could bind the eventHandler to the class it self
LE:
I changed the checkbox logic in the exaple to use a generator, but you can write them one by one
Instead of
{checkboxes.map((id)=>(
<Checkbox
handleCheckboxClick={this.handleCheckboxClick}
selected={this.state.selectedCheckboxes.indexOf(id) > -1}
id={id}
/>
))}
You can write them directly
<Checkbox
handleCheckboxClick={this.handleCheckboxClick}
selected={this.state.selectedCheckboxes.indexOf("item-two") > -1}
id="item-two"
/>
<Checkbox
handleCheckboxClick={this.handleCheckboxClick}
selected={this.state.selectedCheckboxes.indexOf(item-one") > -1}
id="item-one"
/>

Related

How to get value from a dropdown to a button?

I'm using react JS and I have a problem. I don't know how to get the value from my dropdown and put that value into a onclick button. I have read lots of topics but I haven't find anything really useful for a beginner like me.
I am using "scheduler" that helped me built my dropdown and some other stuffs.
So, my dropdown get data from a local file and looks like this:
{values.map(v => (
<option value={this.value}>{v.value}</option>
))}
console.log(ref)
And my button is like this:
<Button onClick={() => this.decrement()}>
Ajouetr la réservation
</Button>
The decrement method was only there to test if it was working, and it is.
Actually, what I want to do is quite simple: I have some values in my dropdown (from 1 to 7). And I have a state that says there is 30 places available. What I want is when I choose a specified item in my dropdown AND validate with my button and then my state to decrement with the specified number. Because right now it only decrement with 1.
I hope it's clear enough for someone to help me, because I spent 2 days on that problem and I don't know what to do.
Thank you :)
Next time, it's nice to provide an interactive example with your question. Here's a CodeSandbox I made that (I hope) illustrates your example (link). If you want to fiddle with the example, just click "Fork" in the top right corner.
Back to the solution:
I think what you're missing is storing the selected value in your state along with the 30 "places". What you want is to make your <select /> tag into a "controlled component". When someone interacts with the <select /> you want to change the internal state so that it matches the selected value. That way, when you call decrement() from your button, you can use the internal state's value rather than getting it from a ref (I think that's what you were trying to do).
Here's a link to the React doc that explains how to use forms, specifically the <select /> tag: (link).
Take care!
I would say that you can think about this in 2 different steps:
SET THE QUANTITY STATE
Set the state with the current dropdown value - For achieving this, you can just use the onChange method in your select:
<select name="quantity"
value={this.state.quantity}
onChange={this.onSelectQuantity}
>
In your constructor, you create a variable quantity inside your state
Create a function called onSelectQuantity where you will set the quantity state with setState.
Do not forget to bind the function onSelectQuantity on the constructor.
With this, every time that you change the value on select, your state would capture its value. You can log it from the function if you want to test if it works.
DECREMENT FROM THE BUTTON
After this, you can just decrease the value of the state again from decrement function
<Button onClick={this.decrement}>
Ajouetr la réservation
</Button>
You will have a function...
decrement() {
const newQuantity = this.state.quantity - 1;
this.setState({
quantity: newQuantity
})
}
Hope it helps!

React - Select multiple checkboxes holding Shift key

I have a list of checkboxes in my React App. When I hold Shift key and click one - the others between current and the nearest checked one should become selected.
For now I'm tring to do somethimg like this:
<input onChange={(e)=>this.handleCheckbox(e)} value={id} checked={this.state.selected.IndexOf(id) > -1} type="checkbox" />
handleCheckbox(e){
if(e.shiftKey){
console.log("shiftKey is hold")
}
//here goes some logic to save checkboxes in the state
}
But the condition if(e.shiftKey) is never executed. What am I doing wrong?
For future visitors:
To check if the Shift key is pressed, check the e.nativeEvent.shiftKey in the click event.
You still have to implement the logic for actually checking the boxes. If you need help with that, let me know.
Hope this helps.
onChange is trigged after you let the key up. You should use the onKeyDown event.

reduxform - how to handle change on a radio button using a single handleChange function

I am using redux-form in my Laravel project (implementing React 15.4.2) and the form I have includes several radio buttons. I need a handleChange function in order for these to remember what is checked (if they are changed from a previous value or selected for the first time) but I don't want to have to write a separate one for each radio button, or implement a huge if else set of rules to handle each one.
Here's one of my fields (just the 'yes' value):
<input type="radio" onChange={this.handleChange.bind(this, 'maintain_licence', 'yes')} checked={maintain_licence.value === 'yes'} id="maintain-licence-yes" value="yes" name="maintain_licence" /><span>Yes</span>
And this is what I have for a single radio button on change, based on the radio button called maintain_licence being interacted with:
handleChange(fieldName, value) {
if(fieldName === 'maintain_licence') {
this.props.fields.maintain_licence.onChange(value);
}
}
This works fine - however, since I have about 20 radios in my form, I can imagine this function getting rather long. Ideally, I'd want something similar to:
handleChange(fieldName, value) {
this.props.fields.fieldName.onChange(value);
}
But as it's trying to find a field actually named 'fieldName' it throws an error stating that it cannot run onChange on an undefined value.
Basically I just need to find a way of using a single function to handle the changes on all radio buttons but I don't know how to do this.
You could use property accessors to get the property you want. Something like:
handleChange(fieldName, value) {
this.props.fields[fieldName].onChange(value);
}
Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_accessors

Passing child's events to a parent

I am enhancing a textarea with autocomplete functionality like this:
<MentionAutocomplete ref="autocomplete">
<Textarea ref="editor"/>
</MentionAutocomplete>
The idea is that MentionAutocomplete can use any thing like textarea or input in itself and still work.
This works nicely and its achieved by the autocomplete listening to keyboard events like this:
<div
className="MentionAutocomplete"
onKeyDown={this.handleKey}
onKeyUp={this.handleKeyUp}
>
{this.props.children}
{this.renderAutocomplete()}
</div>
But I recently ran into an issue, where the autocomplete complains that field.value is undefined. I am assuming that the Textarea component might be doing something, or there might be a browser extension messing with it.
So ideally I would like to do something like this:
var self = this;
<MentionAutocomplete ref="autocomplete">
<Textarea ref="editor" onKeyUp={(event) => self.refs.autocomplete.keyUp(event) } />
</MentionAutocomplete>
But I am wondering, is this the right approach? Is there a better way?
If I understand your issue correctly, you can define the event handler on the parent, and pass it through to the child:
<div className="MentionAutocomplete">
{React.cloneElement(this.props.children {handleEvent: this.handleEvent})}
</div>
handleEvent here is a function you define on the parent where you can pass the event or whatever and is available on the child through this.props.
Another approach that seems a good fit for what you're trying to create would be to create a higher order component which essentially wraps a component with another to add functionality.

How to respond to onChange event for a dropdown in angularify

I'm trying to use angularify in a project, but I'm facing the given problem:
I'm not able to get my onChange function called when a dropdown value changes. I have a second dropdown box which the listed values depends upon the first one.
How can I achieve the intended behavior with angularify?
I was looking, debugging and trying, and looks like I will not be able to do this because de dropdown directive writes a bunch of div's to my html. There is no select elmeent in any place of the document. How will a div element fire a onChange event?
Here is my markup:
Parent select
<dropdown title="Selecione" class="col-6" ng-model="novoProcedimento.procedimento" ng-change="buscarLocaisExecucao(novoProcedimento.procedimento)">
<dropdown-group class="meudropdown" title="proc.nome" value="proc.nome" ng-repeat="proc in procedimentosPermitidos">{{proc.nome}}</dropdown-group>
</dropdown>
Child select - Depends on parent's selection
<dropdown title="Selecione" id="dente-regiao" class="col-5" ng-model="novoProcedimento.local" ng-change="buscarFaces(novoProcedimento.procedimento, novoProcedimento.local)">
<dropdown-group title="local" value="local" ng-repeat="local in locaisExecucao">{{local.descricao}}</dropdown-group>
</dropdown>
Controller - Intendend to be fired on parent's change.
$scope.buscarLocaisExecucao = function (procedimento){
selecaoProcedimentoService.buscarLocaisExecucao(procedimento)
.then(function(locaisExecucao) {
$scope.locaisExecucao = locaisExecucao;
});
}
Am I using the wrong framework? Am I missing something?
Thank you.

Resources