I made a simple todo app using React.
An unordered list (<ul> is used to present all the items in my list. Within that are list items (<li>), and with a list item is a button with an onClick handle attached to a class method:
<button onClick={this.onHandleClick.bind(this)}>Add</button>
In the method, I need to prevent the default behaviour to prevent the page refreshing when the button is clicked:
onHandleClick(event) {
event.preventDefault();
...
}
My question is, what can I do if I want to pass a value into onHandleClick, given that event is given as the argument?
Wrap your event handler in another function and pass item specific parameters this way:
<button onClick={e => this.handleClick(e, itemId)}>Add</button>
handleClick(event, itemId) {
event.preventDefault();
console.log(itemId);
}
I'm not familiar with React, but if the value is static, you could do something like this:
<button onClick={function(){self.onHandleClick(passedInValue);}}>Add</button>
Note that "self" would need to be bound to whatever object contains the 'onHandleClick' method.
Related
It's a basic question but I searched for a guide without success...
I want to have a list of dropdowns and inputs and each dropdown will change the input next to it.
var list = [{ name: "foo1"}, {name: "foo2"}];
return (
{list.map( (name) => {
return (<div>
<dropdown data={someData}
onChange={(ev) => {
if(ev.value == 'clearIt')
<CHANGE THE NEAR INPUT VALUE>
}
}/>
<input value={name.name} />
</div>)
})});
I don't want to use DOM nor ref cause I understood that it's better to avoid it.
Any suggestions?
Or maybe the ref is the only option?
Thanks
So you can achieve this by doing the following steps:
Create a new component and move the dropdown and input to this new component.
Add state to your component by following this example:
https://reactjs.org/docs/state-and-lifecycle.html#adding-local-state-to-a-class
Add an event listener onChange to the dropdown with an event handler which can update the state you created in the first step. (Remember to bind the handler in the constructor.
Add the new component within the div element of this example you gave and pass the relevant data you need to the new component you created.
This should allow you to update only the input next to the dropdown. Also it allows you to have different data for each dropdown you created.
I have a material-ui Table and have been implementing multi-select functionality on it.
My multi select should behave as follows :
Select checkboxes only appear at the start of a row on hover (when nothing has been selected)
Once one row has been selected, all other checkboxes become permanently visible.
The checkbox in the TableHead will select / deselect all rows in the Table when toggled.
Each row has its own individual unique id.
I maintain a Set called idsOfSelectedRows via a useState hook to keep track of all rows currently selected.
Each TableRow has a Checkbox which looks like this
<Checkbox
className={
idsOfSelectedRows.size === 0 &&
styles.rowSelectionCheckboxStyle
}
onChange={() => handleRowSelectionCheckboxClick}
checked={idsOfSelectedRows.has(row.id)}
/>
and handleRowSelectionCheckboxClick looks like this
const handleRowSelectionCheckboxClick = event => {
const idOfClickedRow = event.target.attributes.getNamedItem("id").value;
//If the checkbox has just become checked then add it to the idOfSelectedRows Set, otherwise remove it.
setIdsOfSelectedRows(
event.target.checked
? new Set(idsOfSelectedRows.add(String(idOfClickedRow)))
: () => {
idsOfSelectedRows.delete(String(idOfClickedRow));
return new Set(idsOfSelectedRows);
}
);
};
My issue is that clicking on the checkboxes on the rows is unresponsive. I can select them all by clicking on the select all Checkbox in the TableHead but clicking on checkboxes in individual rows does not change their state ?
Here's a full CodeSandbox reproducing the exact issue. What do I need to do to make the checkboxes in the rows be toggleable ?
Remove the () => from the onChange assignment for a quick fix.
Why
You're declaring an anonymous inline function, but that function does not call anything (missing () that is syntax to call a function). onChange accepts a function reference. When you give it an inline function, that's the function it will call. The other option is to only pass it the function reference of handleRowSelectionCheckboxClick, and then that function will be called on change instead of the anonymous function middleman.
So your two options are:
onChange={handleRowSelectionCheckboxClick} // Just the reference
OR
onChange={(e) => handleRowSelectionCheckboxClick(e)} // New inline function that calls your function
The second way is unnecessary in most cases. The first is preferred unless you need to pass custom parameters. Here is an example of how it might be useful:
onChange={(e) => handleRowSelectionCheckboxClick(e, id)}
In your sandbox, you map over elements and each gets a change handler. To treat them separately in the handler, you can pass an extra variable to the function like above.
For example, I got your sandbox working by changing the handler and assignment to this:
const handleRowSelectionCheckboxClick = (event, idOfClickedRow) => {
...
onChange={(e) => handleRowSelectionCheckboxClick(e, row.id)}
This line: event.target.attributes.getNamedItem("id").value;
won't give you the required id, since there isn't one passed, change the chekbox like so:
<Checkbox
id={row.id}
onChange={handleRowSelectionCheckboxClick}
checked={idsOfSelectedRows.has(row.id)}
/>
Then, retreive the ID like so:
const idOfClickedRow = event.target.attributes.id.value;
Updated codesandbox:
Like the other answers suggest, change the handleRowSelectionCheckboxClick function call like shown in the example.
If you like your solution with an anonymous function, you should add the (): onChange={() => handleRowSelectionCheckboxClick()}
I am using react fontawesome in my project. Under the render function of my component I have the following which maps a school onto the page and then I want a button with an edit icon to perform an action. My code for the button looks as below:
{ this.state.myschools.map( (school) => (
<Row key={school.id}>
<Col><p>{school.name}</p></Col>
{ school.deaths.length > 0 ? <Col md="2"><button id={school.id} className="likelink" onClick={this.goToSchoolDeathList}>View Deaths</button></Col> : <Col md="2"></Col> }
<Col><button id={school.id} onClick={this.editSchool}><FontAwesomeIcon icon={faEdit} /></button></Col>
</Row>
))}
The editSchool function is as:
editSchool(e) {
console.log("School Id");
console.log(e.target.id);
}
If I click on the empty space around the font awesome icon then the id is logged to the console. If I only click on the area where the icon exits then the id is not logged. I want the user to be able to click on any part of the button including the fa icon and to capture the school id.
I have tried adding the onClick event and the id attribute to the "FontAwesomeIcon" component, but that still doesn't work.
Can anyone help?
Well, this is one of the common pitfall you learn during your time with React. Rest assure, we have all fallen into this pit.
There are few AHA moments here.
1st Aha: React doesn't have a usual way to let you get element attribute in an event (an e in your example).
That being said, you can still access to the button element by e.target and use JavaScript to get attribute.
Solution #1 JavaScript getAttribute
editSchool(e) {
console.log("School Id");
console.log(e.target.getAttribute('id'));
}
e.target will be the HTML Element that you click, and we can use JavaScript way to get attribute. (Please Note that, Javascript way is not React way)
But there are another way and you'll see this way more often.
Solution #2 You can define a function directly in a render
<button id={school.id} onClick={e => this.editSchool(school.id)}>
Please be careful about this another pitfall,
2nd Aha: Bind onClick to function, not result of a function.
Don't do this
<button id={school.id} onClick={this.editSchool(scholl.id)}> // This is incorrect
The thing is onClick prop expect a function to call when someone click.
The first one we define a function e => this.editSchool(school.id) and bind onClick with the newly defined function.
While the second, we just bind onClick to a "result" of function this.editSchool(school.id), which is not a function.
I have a modal with some input fields. I can easily pass the data automatically with the user typing an input, using onChange function in the input field, as
<Input onChange={this.props.store.handleCategoryChange} type="text" />
and .. (Using a mobx store, irellevant though)
#observable categoryValue;
#action handleCategoryChange = (e, data) => {
this.categoryValue = data.value;
e.preventDefault();
};
However, I want to add a function where the user can pre-fill this with information elsewhere in the application. I have the data to pre-fill the input fields with, but I can't figure out how do actually input it programatically, without the user doing it?
I need to invoke the above handleCategoryChange function. But my variable would be equal to data.value .. Which presents a problem! Invoking this function by myself isn't possible, because I won't have the event e nor a value called data.value as I will "just" pass in a variable by itself.
What's the right way to programatically fill an input field automatically in React, using variables elsewhere? I need to invoke the onChange function somehow, but the input values will be different..
Use controlled component for this situation, define a value property of input element like this:
<Input value={variable_name} ....
Whenever you will update that variable, automatically that value will get populated in input element.
Now you can populate some default value by assigning a value to variable_name and user can update that value by onChange function.
As per DOC:
An input form element whose value is controlled by React in this way
is called a "controlled component".
Pass in the value property for input:
<input type="text" value={this.state.value} onChange={(e) => {this.setState({value: e.target.value })}/>
you can use the controlled component and pass the value to it.
<input type="text" value{this.state.value}
onChange={()=> {this.setState({value:e.target.value })}}
Good question. I'm having the same issue, and just found a solution.
The problem is that:
You can't just use the default state of the modal component to set the initial input value, because the modal renders one time within the parent component (starting off invisible), so the default state of the Modal wont keep up with any changes to the 'pre-filled' info in the store that the inputs within the modal require access to.
You can't use the value attribute of the input to reference some redux store prop, since this is needed to reference the onChange function so the user can make changes to that pre-filled info.
And, you can't use the onChange function to set the initial value, because it is required to update the local state with the users changes - not set an initial value. Anyway, this requires the user to click on something, and you want the modal inputs to be pre-populated before the user does anything as soon as the modal opens...
So. What we need is to update these input fields every time the Modal attribute isModalOpen (or whatever you are using) changes.
(ie, pre-populate the fields when the Modal is opened).
Again, note that opening the Modal is not RENDERING the modal, it was already rendered, once, and has sat there being invisible until that isModalOpen attribute changed to true.
The Solution:
Step 1: make a handler function in the Modal component that prefills the inputdata by updating the local state of the Modal component. Mine looks like this :
handlePreFillModalInputsOnOpen = () => {
this.setState(() => ({
orthForm: this.props.matchLexUnit['orthForm'],
orthVar: this.props.matchLexUnit['orthVar'],
oldOrthForm: this.props.matchLexUnit['oldOrthForm'],
oldOrthVar: this.props.matchLexUnit['oldOrthVar'],
source: this.props.matchLexUnit['source'],
namedEntityCheck: this.props.matchLexUnit['namedEntityCheck'],
styleMarkingCheck: this.props.matchLexUnit['styleMarkingCheck'],
artificialCheck: this.props.matchLexUnit['artificialCheck'],
error: ''
}));
};
Step 2: Make that function fire ONLY when the isOpen attribute of the modal changes from false to true.
(This is the meat of your problem I think).
Use the lifecycle method componentDidUpdate. This will execute every time the props of the modal change.
componentDidUpdate(prevProps) {
if (this.props.isModalOpen === true && prevProps.isModalOpen === false) {
this.handlePreFillModalInputsOnOpen();
}
}
Watch out
make sure that you have a conditional in componentDidUpdate, otherwise you can run into infinite-loop/performance issues
if you have the possibility of null values icoming in as the prefilled input info, make a null-check so that they will not be passed into the local state.
Hope this helps :)
How to Programmatically Prefill, Fill or Update input fields value in React or useState Hooks?
Firstly - Get and define the data from database
const **yourData** = isDataFromDatabase;
or if the data is stored in Redux, then...
const **yourData** = useSelector(isDataFromDatabase);
Secondly - append it as the default value of a useState hook
const [isDataValue, setIsDataValue] = useState(**yourData**);
Thirdly - create a function to watch and Update the changes made by the user to your data value and set it to the useState hook created above
/** handles Your Data Value */
const handleDataValue = (text) => {
setIsDataValue(text);
};
Lastly - in your input tag, use the useState State as the "Value" parameter so it can be updated with the onChange function
<input
className="css"
id="myDataInput"
type="text"
value={isDataValue}
placeholder={isDataValue}
onChange={(e) => handleDataValue(e.target.value)}
/>
Now when you load the component, the prefilled value will be shown and can be updated in the HTML Input field.
That's All.
Update: JSFiddle http://jsfiddle.net/OrganicCat/CjH87/6/
I have an area that is populated by a normal observable array, and when a button is clicked, an asynchronous service call is made that repopulates that array, but with more data.
This also causes a hidden dom element to display and displays some of that array data there.
Everything works fine until the observable array data is updated, it closes the hidden dom element. It is this event inside Knockout.js(library) that triggers it:
// Ignore writes if the value hasn't changed
if ((!observable['equalityComparer']) || !observable['equalityComparer'](_latestValue, arguments[0])) {
observable.valueWillMutate();
_latestValue = arguments[0];
if (DEBUG) observable._latestValue = _latestValue;
observable.valueHasMutated(); // This event HERE
Is there a way to prevent this from closing the custom binding? Could anything in the binding be causing this? Here is my custom binding handler:
ko.bindingHandlers.expandAmenities = {
init: function (element) {
$('.expandable.closed').hide();
$('.itineraryRowMain .t-go .toggle-expand-rowAmenities').unbind('click').on('click', function (e) {
var $itin_body = $(this).closest('.module-admin-group');
if ($itin_body.hasClass('closed')) {
$(this).parent().parent().next().show();
self.bindAmenities(); // Bind amenity details on open
//$(this).children().html('-');
} else {
$(this).parent().parent().next().hide();
//$(this).children().html('+');
}
$itin_body.toggleClass('open closed');
});
}
};
To summarize, the expand area has a clickable element that will show more data. When this updates the array (just a plain old self.listofStuff(arr);) this causes the new area to get hidden again.
I've figured out the answer. So the problem is that if you bind an array that generates DOM elements (like a list or whatever) and you have elements inside that array that you want to update without doing a .push and without resetting the entire array with self.myArray(newArray) then you have to use an observable variable within the array and update that.
When you modify the observable, it will not redraw the entire array, thereby keeping your dynamically altered elements (like visible/hidden divs within the DOM array) in the same state if they were altered by jQuery or whatever.