I need simple functionality: show Button when value is more than 0. And for this I use code below.
I created some Text Fields with similar states (4) and I don't understand why only in 3rd this didn't work.
My code:
export default function TextFields() {
...
const [showButton3, setShowButton3] = useState("");
...
const handleChange = (event) => {
setShowButton3(event.target.value);
console.log("value is:", event.target.value);
};
return (
<InputOutlined
type={"text"}
id={"text3"}
name={"text3"}
value={showButton3}
onChange={handleChange}
leftElement={
<Img
width={36}
height={36}
radius={12}
src={
"https://images.unsplash.com/photo-1665174271625-178021f8b1a5?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1160&q=80"
}
/>
}
rightElement={
showButton3 ? (
<IconButton
icon={<MdClose size={24} />}
variant={"surface"}
type="reset"
onClick={() => setShowButton3("")}
></IconButton>
) : null
}
>
Your name
</InputOutlined>
)}
I have checked Component, here I add some text
And when I want to clear value, I get this
You can see, value is cleared. But I still see it in my input. How to fix that? Or maybe I doing something wrong?
Proof:
This is very similar Components. I changed id, but I don't understand why value isn't removed. Maybe I need to use useRef or useId. But I have 4 different inputs and only 1 have this issue.
Related
I have a Checkbox component looking like this:
...
{values.map((value) => (
<div key={value}>
<div
className={`checkbox ${
checked && checked.includes(value) && "checked"
}`}
>
<div className="checkbox__box">
<input
{...register(name, {
required: required,
validate: CustomValidation,
})}
id={name + value}
type="checkbox"
name={name}
value={value}
defaultChecked={defaultData === value}
/>
</div>
</div>
<label htmlFor={name + value}>
<FormattedMessage id={value} />
</label>
</div>
))}
As you can see, the component relays on a checked prop array to add the checked class to the single checkbox element.
The component is being called like so:
<Checkbox
values={SourceOfFundsList}
name="source_of_funds__list"
md={12}
register={register}
errors={errors}
defaultData={
props.data.dataTree.customer.source_of_funds.source_of_funds__list
}
checked={source_of_funds__list__checked}
/>
The checked props is populated by watching the form state, like so:
const source_of_funds__list__checked = watch("source_of_funds__list");
This works as expected, adding the class to every item of the Checkbox once it is clicked. It also logs in the console correctly, by using useEffect, like so:
useEffect(() => {
console.log(source_of_funds__list__checked);
}, [source_of_funds__list__checked]);
The problem arises when I move to another route, and then I come back.
When I come back to the page, 2 things happen:
The state is persistent, and when inspecting the component, the defaultData contains the correct array with the checked items
The checked array is undefined, and therefore the UI doesn't display the correct state (all checkboxes looked unchecked, as class is not applied)
I have tried to add a second useEffect to populate the checked array on load, if the defaultData is there to start with, and it consoles correctly, but still the classes are not applied.
useEffect(() => {
if (props.data.dataTree.customer.source_of_funds.source_of_funds__list) {
source_of_funds__list__checked =
props.data.dataTree.customer.source_of_funds.source_of_funds__list;
}
console.log("source_of_funds__list__checked", source_of_funds__list__checked);
}, []);
I'm pretty sure it has to do with the render cycle and some mistake on my use of useEffect.
Any pointers on what could I be missing?
I think you misuse the react hook form. inside the useffect, use the 'setValue' to update the initial array, and instead of using 'watch', replace it with 'useWatch'. Try it out, hope it helps you!
Having a hard time seeing how I could accomplish this. I created some custom number buttons from 0-9 that users can click on instead of using the keyboard. The problem I'm having is I have multiple dynamically created input fields depending on JSON Data so let's say there are 10 dynamically created input fields and a user starts with question one and the user then uses the custom number buttons I created and clicks numbers "145" to answer question one, but what happens is then all 10 inputs have the same number "145" not the problem the user was trying to solve. I'm using the context API to then save the values typed in on a function called getButtonValue that I then call to the parent component and save the values in a state array, so I know that my problem is that all the inputs share the same state array but how could I make sure the correct input the user clicks on is only receiving those values.
Thanks in advance.
My Custom Number Button Component:
import { FormContext } from "../../lib/FormContext";
function ActivityBar() {
const { getButtonValue } = useContext(FormContext);
return (
<div className={`${activity.activity__workSheet__numberButton}`}>
<button value={0} onFocus={(e) => getButtonValue(e)}>
<img
className={`${activity.activity__workSheet__img0}`}
src={"/assets/activityNumber-btn.png"}
alt="activity number button"
/>
.... more code
Parent Component:
const [numberButtonClicked, setNumberButtonClicked] = useState([]);
const getButtonValue = (e) => {
setNumberButtonClicked((prevButtonClicked) => [
...prevButtonClicked,
e?.target?.attributes[0].value
]);
};
return (
<Carousel>
<div ref={imageRef} style={{ height: "100%" }}>
{Object.entries(elements).map((element, i) => {
const { fields } = element[1];
if (fields) {
return (
<Element
key={i}
field={fields[0]}
id={i}
useReff={`answer${i}`}
currentValue={
numberButtonClicked === "" ? null : numberButtonClicked.join("")
}
/>
);
} else {
return;
}
})}
</div>
</Carousel>
Got a good working version figured out for this scenario, what I did was.
I have a onFocus method on my input tags that then takes in the event and calls a handleChange(e) function. Within that function I then save the currentInputId in a variable by using e?.target?.attributes[0]?.value and the previous InputId in a state variable and just check if the previous InputId is equal to the currentId user just focused on. If so then we'll add the next number user clicks into the same field, else if previousInputId !== currentInputId then make my user value state array empty, setNumberButtonClicked([]).
I am mapping through an array and displaying its data. I have edit button to modify that data. When I click edit button, dropdown shows and I am able to edit as shown in screenshot I shared. Open this screenshot
Problem is all of edit buttons work even when I click any one, I want only clicked edit button to work rather than all. How can I achieve this functionality?
This is my code:
const [show, setShow] = useState(false);
const handleShow = () => { setShow(!show); };
<p onClick={handleShow}>
Edit
{show === true ? (
<IoIosArrowUp style={{ color: "#F1BB0F" }} />
) : (
<IoIosArrowDown style={{ color: "#F1BB0F" }} />
)}
</p>
{show && (
<div>
<p>
Unlocks: {lockParams[index]?.endDateString}
</p>
<p>
Unlocker : <span>{lockParams[index]?.unlocker}</span>
</p>
</div>
)}
If you have N different buttons, and you want the view to be different depending on which of those buttons are toggled on or off, you should have state corresponding to those N buttons. An array of booleans would make sense here. That way, if, say, the 4th button is toggled, you can update the 4th element in the state array, and then when the component renders, while iterating over the 4th index, it can look at that 4th boolean to see what should be shown there.
You haven't shown the necessary code in the question, but, for example, if you have:
someArray.map((item) => (
// some JSX
<p onClick={handleShow}>
// etc
Then you need to initialize a state array containing the same number of elements as someArray, like this:
const [shows, setShows] = useState(() => someArray.map(() => false));
const handleShow = (i) => setShows((show, j) => j === i ? !show : show);
And then use index when rendering:
someArray.map((item, i) => (
// some JSX
<p onClick={() => handleShow(i)}>
Edit
{shows[i] ? (
<IoIosArrowUp style={{ color: "#F1BB0F" }} />
) : (
<IoIosArrowDown style={{ color: "#F1BB0F" }} />
)}
</p>
{shows[i] && (
<div>
<p>
Unlocks: {lockParams[index]?.endDateString}
I am currently working with javascript/React and I have some problems with the last one.
In my window, I have several buttons, and below, a dropdown list. The idea is to disable this dropdown list. It is only enabled once you click a button, based on the disabled parameter. Among the button parameters, there is also a onClick() which does something else (but at the beginning of this function, I implemented some code so that the value in disabled would change). Which is why I want to use the disabled parameter to enable/disable the dropdown list.
So this is supposed to be easy...
Here is a part of the html :
<DropdownButton
title={"Type : "}
className="sequence-dropdown"
disabled={true}
key="sequence-dropdown"
id="sequence-dropdown"
>
<MenuItem onClick={() => this.changeValue(value1)}>Value 1</MenuItem>
<MenuItem onClick={() => this.changeValue(value2)}>Value 2</MenuItem>
<MenuItem onClick={() => this.changeValue(value3)}>Value 3</MenuItem>
</DropdownButton>
And here is the code supposed to enable the dropdown list, contained in the function changeValue().
var statusDropdown = document.getElementById('sequence-dropdown').disabled;
if (statusDropdown === true) {
console.log(document.getElementById('sequence-dropdown').disabled)
document.getElementById('sequence-dropdown').disabled = false;
console.log(document.getElementById('sequence-dropdown').disabled)
}
else if (statusDropdown === false) {
//console.log(statusDropdownMapType)
document.getElementById('sequence-dropdown').disabled = true;
//console.log(statusDropdownMapType)
}
I did not try it, but I think I could simply use :
document.getElementById('sequence-dropdown').disabled = !document.getElementById('sequence-dropdown').disabled
But that's not the issue here.
My problem is : the button is well disabled at the beginning (with some grey color indicator). When I click a button, the grey color disappears, the component style is normal, and the disabled parameter is well changed.
BUT when I click on the dropdown, the list does not appear... It is like I was just clicking a button, nothing happens...
Does anyone know why ?
I think you should use
document.getElementById('sequence-dropdown').setAttribute("disabled","disabled");
and
document.getElementById('sequence-dropdown').removeAttribute("disabled");
regards Halliballi
If you set disabled={true} everytime the component renders it will be disabled. I would use another approach, like this:
constructor() {
super();
this.state = {disabled: true};
}
...
render () {
const {disabled} = this.state;
return (
<DropdownButton
title={"Type : "}
className="sequence-dropdown"
disabled={disabled}
key="sequence-dropdown"
id="sequence-dropdown">
<MenuItem onClick={() => this.changeValue(value1)}>Value 1</MenuItem>
<MenuItem onClick={() => this.changeValue(value2)}>Value 2</MenuItem>
<MenuItem onClick={() => this.changeValue(value3)}>Value 3</MenuItem>
</DropdownButton>
);
}
Then, since I can see you only change the state from disabled to enabled and viceversa, when you click your button this would be the approach:
this.setState((oldState) => {
return {disabled: !oldState.disabled};
});
This way is more React. Hope it helps.
I recently upgraded to redux-form v6, and my material-ui custom datetime picker stopped working. Here's what it looks like now:
class DateTimePicker extends React.Component {
handleDateChanged(e, value) {
let currentTime;
if (this.props.dateField.value) {
let currentValue = moment(this.props.dateField.value);
currentTime = currentValue.subtract(currentValue.clone().startOf('day'));
}
let newValue = moment(value).startOf('day').add(currentTime || 0);
this.props.dateField.onChange(newValue.toDate()); //this.refs.timePicker goes undefined when this line runs.
this.refs.timePicker.openDialog();
}
handleTimeChanged(e, value) {
return this.props.dateField.onChange(value); //This is called, but the value does not change.
}
formatDate(value) {
return moment.tz(value, this.props.timezone).format('M/D/YY HH:mm zz');
}
render() {
return (
<span>
<DatePicker
value={this.props.dateField.value || null}
autoOk={true}
maxDate={new Date()}
floatingLabelText="Change At"
floatingLabelStyle={{pointerEvents: 'none'}}
errorText={this.props.dateField.touched && this.props.dateField.error}
formatDate={this.formatDate.bind(this)}
onChange={this.handleDateChanged.bind(this)}
/>
<TimePicker
style={{display: 'none'}}
value={this.props.dateField.value || null}
format="24hr"
hintText="Time"
ref="timePicker"
onChange={this.handleTimeChanged.bind(this)}
/>
</span>
);
}
}
And it's used in the form like so:
<Field
name="changeAt"
component={({input}) => {
return <DateTimePicker dateField={input} timezone={this.props.driver.homeTerminal.timezone} />
}}
/>
I'm having two distinct problems.
First problem: When I pick a day, handleDateChanged is called as expected. When the method starts, this.refs.timePicker refers to the time picker as expected. However, when the line above it is executed (this.props.dateField.onChange(newValue.toDate())), then refs are lost and and this.refs.timePicker changes to undefined.
Each subsequent time time I pick a date, refs are not lost, and it works correctly.
Second problem: When I pick a time, handleTimeChanged is called which calls this.props.dateField.onChange(value) with the correct value; however the value is not changed.
Any suggestions are appreciated.
I'm new to this stuff so I don't know whether I'll be much use. But addressing the second problem: The only difference between my code and yours is that I'm doing<DatePicker ... onChange={this.props.onChange} />. Alternatively you could do
constructor(){this.handleChange = this.handleChange.bind(this)}
and then remove the bind in the render() method. The render method gets called every time the (props?) change and perhaps the issue is to do with this bind.
The docs on this: http://redux-form.com/6.0.0-rc.4/docs/faq/CustomComponent.md/
I'm also using a react element instead of using a component, and it comes out quite cleanly.
const DateInput = ({ input, label, meta: { touched, error } }) => (
<DateTimeField onChange={input.onChange} />
);
with
<Field name="date" component={DateInput} label="Date & Time Starting" />
The above is working, so perhaps you could try iterating from there and adding code until the problem is reproduced?
Answer right at the bottom and work your way up to suit various implementations:
https://github.com/erikras/redux-form-material-ui/issues/37
It's wanting an object, more specifically a Date object.
format={(value) => value === '' ? null : new Date(value)}