I'm trying to add a class to a div wrapping an input on focus with Redux and React.
My React component looks like this:
import React from 'react'
const Input = ({ value, onChange }) => (
<div className="">
<input type="email" onChange={onChange} value={value} />
</div>
)
export default Input
When a user focuses on the input, I want the class selected to be added to the div. The component is connected using react-redux.
The two ways I've tried so far:
Attempt 1
Add an onFocus listener to the input and dispatch an action that adds the inputs focus state into the inputs state , for example:
{
value: '',
focused: true
}
If focused is true, add the class with className={focused ? 'selected' : ''}
My concern with this approach is I could get into issues where multiple inputs have focused: true. So I'd have to manage that myself and look through the whole input state and reset to false before setting focus on a new input.
Atempt 2
Add an onFocus listener to the input and dispatch an action that contains the ID of the focused input and store the ID in the state. This would solve the issue of having to reset all other inputs to false.
My concern with this is I'd have to give every input I wanted this feature on a unique ID (this could possibly use Reacts ID). I'd have to add something like className={ID === focusedID ? 'selected' : '')
I feel like there must be a better way to achieve this. Any help would be appreciated.
Thanks
You dont even need Redux for that, just store the "selected" state in the component.
Something like this
var InputComp = React.createClass({
getInitialState: function() {
return {focus: false}
},
onFocus : function(){
this.setState({focus: true})
},
onBlur: function(){
this.setState({focus: false})
},
getClass: function(){
if(this.state.focus === true)
return "selected";
else
return "";
},
render: function() {
var inputClass = this.getClass();
return <div className={inputClass}><input onBlur={this.onBlur} onFocus={this.onFocus}/></div>
}
});
ReactDOM.render(
<InputComp/>,
document.getElementById('container')
);
jsfiddle
You need to use the :focus pseudo-selector. You need to add a class to your divs, for instance, focusable. If that is done, you need this CSS rule:
.focusable:focus {
/* Your rules for selected */
}
Related
I am using Material Table in Reactjs to show the table Data. Here I am stuck at a part where I want to change state when I click on the edit option/icon. I do not want to change the onClick functionality of Edit button, but I just want to access edit icon. Is there any way I can achieve it?
Note: I only want it when I click on the edit(pen) icon before it becomes the OK and Cancel buttons.
First of all you have to remove the default edit button. Then use a Button in action and tableRef props. Now, all you have to do is put the edit's code in onClick event like this:
var _interopRequireDefault = require("#babel/runtime/helpers/interopRequireDefault");
var _objectSpread2 = _interopRequireDefault(
require("#babel/runtime/helpers/objectSpread")
);
...
export default function MaterialTableDemo() {
const tableRef = useRef(null);
...
return (
<MaterialTable
...
tableRef={tableRef}
actions={[
{
onClick: (event, rowData) => {
console.log("Clicked", rowData);
tableRef.current.dataManager.changeRowEditing(rowData, "update");
tableRef.current.setState(
(0, _objectSpread2["default"])(
{},
tableRef.current.dataManager.getRenderState(),
{
showAddRow: false,
}
)
);
},
},
]}
...
/>
)
}
This part is the requirements that material-table uses in it's code:
var _interopRequireDefault = require("#babel/runtime/helpers/interopRequireDefault");
var _objectSpread2 = _interopRequireDefault(
require("#babel/runtime/helpers/objectSpread")
);
In addition this is the code that you need to fire edit action:
tableRef.current.dataManager.changeRowEditing(rowData, "update");
tableRef.current.setState(
(0, _objectSpread2["default"])(
{},
tableRef.current.dataManager.getRenderState(),
{
showAddRow: false,
}
)
If this html template is written by you then you can wrap icon in some parent element and add click event on parent.
I have a dropdown menu on a form. Once the user chooses a selection, I'd like for the Submit button to do different requests based on the dropdown menu item selection. For example, if user chooses Apples, then Submit button fires a function1, while if user chooses Bananas, then Submit button fires a function2.
I have a parent component that looks like this:
class Container extends React.PureComponent {
_onSubmit = (date) => {
this.props.function1({
paramA: this.props.paramA,
paramB: this.props.paramB,
});
};
render() {
return (
<div>
<DownloadForm
dataSetName="Apples"
buttonText="Request Report"
onSubmit={this._onSubmit}
/>
</div>
);
}
}
Now, my DownloadForm component, which is nested here, will have a dropdown menu, and based on the dropdown menu's item, will have the onSubmit do something with a function2, which takes different parameters too.
I'm not sure how to do design this. I have 1 page as above, with the parent component that passes the onSubmit to it's nested component, and fires away to function1. I have another page as above, with the parent component also passing an onSubmit to the nested component, which fires away to function2.
How do I make it so that the dropdown menu item selection, inside the nested component, will then change it's button's onSubmit to fire away to function1 or function2 based on the dropdown menu item selected?
I hope my issue is clear.
Have a state that records the value of the dropdown selection inside the parent component. Inside _onSubmit() you check the state.
if (this.state.selection == "Apples") {
function1(param1, param2)
}
Here is a quick bit of code I put together that should get you started. It also takes into account some of #Mikes' comments
class Container extends React.Component {
constructor(props){
super(props);
this.state = {
selection: null
}
}
onSubmit = (date) => {
if (this.state.selection == "Apples") {
this.props.function1({
paramA: this.props.paramA,
paramB: this.props.paramB,
});
}
if (this.state.selection == "Bananas") {
this.props.function2({
paramA: this.props.paramA,
paramB: this.props.paramB,
});
}
};
handleSelectionChange = (selection) => {
this.setState({selection: selection})
}
render() {
return (
<div>
<Dropdown
onChange={this.handleSelectionChange}
/>
<DownloadForm
dataSetName="Apples"
buttonText="Request Report"
onSubmit={this._onSubmit}
/>
</div>
);
}
}
I have a listener to check what input was selected at last to add some kind of string/variable later into it.
created: function () {
document.addEventListener('focusin', this.focusChanged);
}
focusChanged(event) {
if (event.target.id !== 'variable-search') {
this.lastElement = event.target;
}
}
This seems to work fine, and when I click on an input field this.lastElement gets updated with the focused element. All these inputs have a v-model which can be a string in an object or just a plain string.
Now the thing is when I try to update the value by:
this.lastElement.value += variable;
Vue won't detect its changes, also in the Vue Developer tools the string won't get updated. But in the input field it does get updated. So this should be a reactivity thing.
When I add a new character into the input field (v-model) it does update again. So it's just when I update the string by this.lastElement it won't register its changes.
The thing is that the input fields are dynamic, so I don't know how many input fields are here and how many lists etc. So I need Vue to re-render the variable after the value of lastElement is updated.
Edit
I just tried it with an #focus here an example
<input v-model="testVar" #focus="lastElement = testVar">
If I update lastElement later on it doesn't update it for testVar but just for lastElement.
Changing values in DOM elements programmatically does not cause DOM events to fire. v-model relies on input (or change when using .lazy) events to update its bound variable. If you dispatch those events when you update the value in an input, the variable will react.
new Vue({
el: '#app',
data: {
items: ['one','two','three']
},
methods: {
addAddress() {
this.lastElement.value += 'address';
this.lastElement.dispatchEvent(new Event('input'));
this.lastElement.dispatchEvent(new Event('change'));
},
focusChanged(event) {
this.lastElement = event.target;
}
}
})
<script src="https://unpkg.com/vue#latest/dist/vue.js"></script>
<div id="app">
<div v-for="item, index in items">
<input v-model="items[index]" #focus="focusChanged">
{{item}}
</div>
<button type="button" #click="addAddress">+address</button>
</div>
You could add a ref attribute to each of the inputs and use the ref to update their values. For example, an input element could be:
<input v-model="testVar" ref="input1" id="input1" #focus="focusChanged">
In your methods:
methods: {
focusChanged(event) {
if (event.target.id !== 'variable-search') {
this.lastElement = event.target.id;
}
},
}
And where you want to update the value: this.$refs[this.lastElement].value += variable;
I am fetching some data from the server to populate a list of items, and each item got a onClick event binded to the items id, that changes the UI to be disabled when clicked.
My problem is that the UI changes to disabled perfectly on the first click, but when I go on to click on the next item it resets the first one, so there is only one button disabled at a time. How do I make it so I can disable all the items I want, without resetting the previous state?
class Video extends Component {
constructor () {
super()
this.state = {
isDisabled: false
}
}
handleClick(frag, voted, event){
event.preventDefault()
this.setState({
isDisabled: {
[frag]: true
}
})
}
Snippet of what I return in the UI that changes the disabled button
<button onClick={this.handleClick.bind(this, frags.id, frags.voted)} disabled={this.state.isDisabled[frags.id]} className="rating-heart-2">
<i className="fa fa-heart" aria-hidden="true"></i>
</button>
I would really appreciate all tips!
You are completely replacing isDisabled each time you update state, but you really just want to add a new property to it. Try the following:
handleClick(frag, voted, event){
event.preventDefault();
let {isDisabled} = this.state;
isDisabled[frag] = true;
this.setState({isDisabled});
}
I am using React to create a controlled select element. My app validates the new option value selected by the user and then updates that value on the element.
The validation takes some time and the behavior I observe is as follows:
User selects an option
Element changes the selected option to index 0
Once the validation period is over, the element updates itself to the new value
I do not understand why the element reverts to the first index and how can I prevent this (except by setting the value immediately which is undesired in my application)
Use this JSFiddle to see this behavior (select Option2) https://jsfiddle.net/qo39g2j4/
var Select = React.createClass({
render: function() {
return (
<select id="my-select" value={this.props.value} onChange={this.props.onChange}>
<option value="Option1">Option1</option>
<option value="Option2">Option2</option>
<option value="Option3">Option3</option>
</select>
);
}
});
var Wrapper = React.createClass({
getInitialState: function () {
return {value: "Option3"};
},
onChange: function (event) {
var value = event.target.value;
setTimeout(function () {
this.setState({value: value});
}.bind(this), 1000); // this simulates the validation period
},
render: function () {
return (
<Select
value={this.state.value}
onChange={this.onChange}/>
);
}
});
ReactDOM.render(
<Wrapper/>,
document.getElementById("container")
);
Try using defaultValue instead of value in your Select component. Or add event.preventDefault() in onChange.
I had the same problem when integrating redux-form within my app, due to an issue in onBlur event handler,
so here is how i handled it using customSelect react component :
export default class CustomSelect extends Component {
render() {
const { children, value, onBlur, ...props } = this.props
return (
<select
{...props}
onBlur={() => onBlur(value)}
>
{children}
</select>
)
}
}
Here onBlur eventHandler is explicitly called with the value passed, to keep it selected
Also validation on client-side or server-side should not be represented by a timeout as user is expected to see the wrong input data with the validation message if invalid,
I guess your implementation means that selected option would be visible only if valid which is not the best user experience you can have, if that's the actual case
Solution is based on redux-form react-select bug