I have a functional component that contains user input and it is child of a class component which contains the state with the input. In the child functional component I want a function that can swap values between two TextFields(https://material-ui.com/api/text-field/).
What I have tried so far:
<TextField
id="outlined-from"
label="Από"
className={classes.textField3}
value={transport_detail[idx].from}
onChange={handleDynamicChange('from', idx)}
ref={x => from = x}
/>
<IconButton onClick={()=>{
var temp = from.value;
console.log(temp)
from.value = to.value;
to.value = temp;
}}>
<SyncIcon/>
</IconButton>
<TextField
id="outlined-to"
label="Προς"
className={classes.textField3}
value={transport_detail[idx].to}
onChange={handleDynamicChange('to', idx)}
ref={x => to = x}
/>
I tried using ref, but as I see from the docs it is only used with class components.
Anyone has any solution to this?
Since you are already saving the values of the textfields in the state, you can remove the ref and just change, how you save the texts. This will let you easily swap the contents.
Here is a sandbox, to show you the final result.
Just like in your example, the functional component gets the values and the handleDynamicChange function. This will populate the textfields and updates on changes of the text.
The button simply calls handleDynamicChange for each value with the opposing key:
handleDynamicChange("to", idx)({ target: { value: transport_detail[idx].from} });
handleDynamicChange("from", idx)({ target: { value: transport_detail[idx].to} });
This will set the value of the from text field to the value of the to textfield and vice versa.
Obviously, you need to adjust the values to fit your data structure, but this should work, without the need of refs.
Related
I have a <TextField> component from Material UI that's supposed to allow a user to choose a numerical value. The field is for use on a medical product so we cannot have a default value that's displayed; rather, the value is initially an empty string and onInput we update it accordingly. The min/max props don't work as needed when passed to InputProps, so we manually check if the value passed is less than or equal to zero ('-' also evaluates to true here), and if so we reset the value to 1. However, when I try to instead reset the value to an empty string (i.e. clear the input), the actual state value changes, but the text displayed in the TextField component remains the same for some reason. If anyone knows why the text displayed is not clearing and what I could do to fix this, please let me know!
Code below:
<TextField
disabled={!!requirement.id}
type="number"
value={requirement.val}
variant="outlined"
InputProps={{ inputProps: { min: 1, max: 1000000 } }}
placeholder={'Title!'}
onInput={(e) =>
handleRequirementChange(
Number(e.target.value),
index
)}
/>
const handleRequirementChange = (val, index) => {
const requirement = [...requirement]
if (val <= 0) val = 1
requirement[index] = val
setRequirement(requirement)
}
I am new to ReactJS and pairing it with Material UI is really causing me some roadblocks. I have created a reusable search filter component for my data tables and it worked exactly the way I wanted, but now I want to add a button to clear the field and show the unfiltered results, as well as return the InputSearch component back to its default state so it will display the label inside the field again, not up in the field’s border as these Material UI TextFields do then they are focused or have a current value. This is where I am hitting my roadblock. I have tried multiple solutions I found online, like using the inputRef/useCallback method to change the values, but it didn’t seem to work…or maybe I misunderstood and did it wrong. I was also recommended to put my search values to state. As happens with state my searches are now always one render behind (I.E. , results matching ‘US’ for ‘USA’ , ‘USA’ for ‘USAF’, etc…). Then when I run the handleFilterReset function to set the filter values back to an empty string, nothing happens. I just want my search filter to work instantly (like it did before I moved the value to state [commented out]) and be able to be cleared, resetting the table back to its default display.
Can someone please help me figure this out? Suggestions are appreciated, but code snippets are much more helpful since I am really new to React and especially Material UI.
dataTable.js
const [inputValue, setInputValue] = useState('')
const [searchFn, setSearchFn,] = useState({ fn: items => { return items; } });
// Searching Data
const handleSearch = e => {
setInputValue(e.target.value) // value displayed in input field
let query = (e.target.value).toString().toLowerCase();
setSearchFn({
fn: items => {
if (query === "")
return items;
else
return items.filter(x =>
(x.tankName !== null && x.tankName.toLowerCase().includes(query)) ||
(x.dimensions !== null && x.dimensions.toLowerCase().includes(query))
)
}
})
}
// Clearing Filters
const handleFilterReset = () => {
setInputValue('');
setSearchFn({fn: items => {return items;}})
};
// Search and filter Inputs
<div>
<InputSearch
value={inputValue}
onChange={handleSearch}
/>
<Button
text="Reset"
onClick={handleFilterReset}
/>
</div>
InputSearch.js
export default function InputSearch(props) {
const { inputRef, name, value, error=null, onChange, ...other } = props;
return (
<TextField
label="Search..."
name={name}
value={value}
onChange={onChange}
{...other}
{...(error && {error:true, helperText:error})}
>
</TextField>
)
}
You need to pass the value to InputSearch
Heres an example:
https://codesandbox.io/s/morning-brook-durbvd?file=/demo.tsx
React has a pretty good introduction on its site.
https://reactjs.org/docs/components-and-props.html
The code has been updated with a solution to this issue. I created a display value for the input that I passed to state, which was set to a blank string when the reset is pressed as well as passing an unfiltered data set.
In my React with hooks, I have a small component that renders based on a condition.
The Recipients below component works in this way, if we have more than 1 recipient then we show a dropdown list otherwise we just show the name of the recipient.
The issue is that as you see the first is a checkbox where onChange is passing the selected recipient. Having this I mind when I have only 1 recipient I need to set that recipient in the state as const [recipients, setRecipients] = useState({});
I would like to setRecipients(...) for when we have just 1 recipient but here I blocked and don't know the way of doing it and passing inside that component as you see is just a typography component. Need help to make this.
The onChange as per reference looks like this
const handleRecipientChecked = useCallback(
id => e => {
const selection = { ...recipients };
selection[id].selected = !selection[id].selected;
setRecipients(selection);
},
[recipients],
);
The component
const Recipients = () => {
return Object.keys(recipients).length > 1
? Object.keys(recipients).map(id => (
<div className={classes.dropdownContainer} key={id}>
<FormControlLabel
control={
<Checkbox
key={id}
checked={recipients[id].selected}
onChange={handleRecipientChecked(id)}
/>
}
label={`${recipients[id].name} (${roles[recipients[id].role]})`}
/>
</div>
))
: Object.keys(recipients).map(id => (
<Typography>{`${recipients[id].name} (${
roles[recipients[id].role]
})`}</Typography>
));
};
The same way you are rendering the item based on length (I.e to display checkbox or text) will be the same way you will be setting state. In your usecallback, check if the length is greater than 1 and do the normal set state for check box, while in the else block just set the state to that only item.
When using text input using this.state it doesn't matter to give value a state variable and you'll able to edit the value, in my case here I am rendering a list and when I specify value of text input I can no longer edit that value because it's outside the state, I don't want to use placeholder to show the value, I need a way to have it as value but also be able to edit that value.
<TextInput
keyboardType='decimal-pad'
value={String(roomData.room_attached_dimensions.depth)}
onChangeText={(value) => this.handleRoomEdit(value)}
/>
Also I don't understand why should I wrap my values with String, it shows nothing otherwise.
My list looks like so
const attachedRooms = this.state.rooms_attached.map((item, index) => (
<View key={index}>
...
</View>
))
The function does nothing special
handleRoomEdit = (value) => {
this.setState({ roomEdit: true, value: value })
}
Of course I have different inputs I cannot simply give them different names, the data is stored on asyncStorage and even if I edit the array it dont work unless I re mount the damn component
Okay there's you error. You are not handling your input's value with state. That's why it does not work. You can do something like this -
<TextInput
keyboardType='decimal-pad'
value={this.state.value || String(roomData.room_attached_dimensions.depth)}
onChangeText={(value) => this.handleRoomEdit(value)}
/>
I think this should work. Now you are handling your input's value with state. Or you can use defaultValue to give initial value, but I think react discourages using defaultValue and Value both -
<TextInput
keyboardType='decimal-pad'
defaultValue={String(roomData.room_attached_dimensions.depth)}
value={this.state.value}
onChangeText={(value) => this.handleRoomEdit(value)}
/>
EDIT:: Also you can use ES6 to make your code clean -
handleRoomEdit = (value) => {
this.setState({ roomEdit: true, value }) //use ES6 shorthand //
}
I have a React Native form that allows me to add an Input UI in the form, by clicking a button with this function. This allow me to generate it on the fly. The code for that is this.
addClick() {
this.setState(prevState => ({ values: [...prevState.values, ""] }));
console.log(this.values[0].name);
}
That part works well, but I'm having a problem extracting the data from the dynamic inputs, and add it to an array. So far I have tried this
setVal = value => {
const values = this.state.values[0];
if (values[0].name === "" || values[0].description === "") return;
[...this.state.values, value];
this.setState(values);
console.log(values);
};
How do I organize my states properly so that I can add as many inputs I need, and when I'm finished, I can update the state, and access the new data in my list component?
How do I update my state to the new Array? at the moment, this.state only shows the initial state set at the top.
I'm missing a few things
Please take a look at the full code sandbox HERE so you can see:
See...your created isssue is not so obvious we need to see where you call setVal() function but....
i think you will be more comfortable if you render your <input/> s directly from your state array, not from const x = [] variant. because it seems like you want a dynamic view and in such a cases you will need to bind your loop quantity from state. so:
this.state = {
x: [0,1,2,3,4]
}
and inside your render :
{this.state.x.map(x => {
return (
<TextInput
placeholder={`name${x}`}
value={values[x.toString()]}
handleBlur={() => handleBlur(x.toString())}
onChangeText={handleChange(x.toString())}
style={styles.input}
/>
);
})}