I have a hook like this :
export default function AddSections ({ onCheckItem }) {
const checkItem = (item) => {
console.log('check')
}
return (
<div>
<Checkbox
className="section_item"
key={index}
id={section.name}
name="add-sections"
type="radio"
label={'section.label'}
value={'section.name'}
onChange={val => checkItem(item)}
/>
</div>
)
}
for the first time when i check the checkbox the function return the console.log, when i try to undo the check it never works
Problem is you have defined the Checkbox type to be radio and you are using the uncontrolled input so it doesn't allow your to toggle.
You have two solutions
Change the type to checkbox
sample code:
export default function AddSections ({ onCheckItem }) {
const checkItem = (item) => {
console.log('check')
}
return (
<div>
<Checkbox
className="section_item"
key={index}
id={section.name}
name="add-sections"
type="checkbox"
label={'section.label'}
value={'section.name'}
onChange={val => checkItem(item)}
/>
</div>
)
}
Use Controlled input
sample code
export default function AddSections ({ onCheckItem }) {
const [checked, setChecked] = useState('');
const checkItem = (item) => {
setChecked(checked => (checked == item? '': item));
}
return (
<div>
<Checkbox
className="section_item"
key={index}
id={section.name}
name="add-sections"
checked={checked === item}
type="radio"
label={'section.label'}
value={'section.name'}
onChange={val => checkItem(item)}
/>
</div>
)
}
I suppose that Checkbox component is gonna map the props to native checkbox element. If that is the case, you should use checked prop as well. I don't see any hooks in your code though.
import React, {useState, useEffect} from 'react'
export default function AddSections ({ onCheckItem }) {
const [checked, setChecked] = useState(false)
const handleCheckItem = (item) => {
setChecked(!checked)
}
useEffect(() => {
console.log('clicked')
}, [checked])
return (
<div>
<Checkbox
className="section_item"
key={index}
id={section.name}
name="add-sections"
type="radio"
label={'section.label'}
value={'section.name'}
checked={checked}
onChange={handleCheckItem}
/>
</div>
)
}
Related
I'm creating an Autocomplete component in React.js with the help of Material-UI headless useAutoComplete hook. The component is working properly. When the user tries to type any character, the Listbox will automatically open.
But the problem is that when the user selects anything and pays attention to the input element, the ListBox reopens. How can I prevent this?
Codesandbox:
Code:
import React from 'react';
import useAutocomplete from '#material-ui/lab/useAutocomplete';
function AutocompleteComponent(props) {
const { data } = props;
const [value, setValue] = React.useState('');
const [isOpen, setIsOpen] = React.useState(false);
const handleOpen = function () {
if (value.length > 0) {
setIsOpen(true);
}
};
const handleInputChange = function (event, newInputValue) {
setValue(newInputValue);
if (newInputValue.length > 0) {
setIsOpen(true);
} else {
setIsOpen(false);
}
};
const {
getRootProps,
getInputProps,
getListboxProps,
getOptionProps,
groupedOptions
} = useAutocomplete({
id: 'form-control',
options: data,
autoComplete: true,
open: isOpen, // Manually control
onOpen: handleOpen, // Manually control
onClose: () => setIsOpen(false), // Manually control
inputValue: value, // Manually control
onInputChange: handleInputChange, // Manually control
getOptionLabel: (option) => option.name
});
const listItem = {
className: 'form-control__item'
};
return (
<div className="app">
<div className="form">
<div {...getRootProps()}>
<input
type="text"
className="form-control"
placeholder="Location"
{...getInputProps()}
/>
</div>
{groupedOptions.length > 0 && (
<ul
className="form-control__box"
aria-labelledby="autocompleteMenu"
{...getListboxProps()}
>
{groupedOptions.map((option, index) => {
return (
<li {...getOptionProps({ option, index })} {...listItem}>
<div>{option.name}</div>
</li>
);
})}
</ul>
)}
</div>
</div>
);
}
export default AutocompleteComponent;
You can store the selectedItem in a state and use it in handleOpen to decide whether the list has to be displayed.
For setting selectedItem you can modify the default click provided by getOptionProps
import React from 'react';
import useAutocomplete from '#material-ui/lab/useAutocomplete';
function AutocompleteComponent(props) {
const { data } = props;
const [value, setValue] = React.useState('');
const [isOpen, setIsOpen] = React.useState(false);
const [selectedItem, setSelectedItem] = React.useState('');
const handleOpen = function () {
if (value.length > 0 && selectedItem !== value) {
setIsOpen(true);
}
};
const handleInputChange = function (event, newInputValue) {
setValue(newInputValue);
if (newInputValue.length > 0) {
setIsOpen(true);
} else {
setIsOpen(false);
}
};
const {
getRootProps,
getInputProps,
getListboxProps,
getOptionProps,
groupedOptions
} = useAutocomplete({
id: 'form-control',
options: data,
autoComplete: true,
open: isOpen, // Manually control
onOpen: handleOpen, // Manually control
onClose: () => setIsOpen(false), // Manually control
inputValue: value, // Manually control
onInputChange: handleInputChange, // Manually control
getOptionLabel: (option) => option.name
});
const listItem = {
className: 'form-control__item'
};
return (
<div className="app">
<div className="form">
<div {...getRootProps()}>
<input
type="text"
className="form-control"
placeholder="Location"
{...getInputProps()}
/>
</div>
{groupedOptions.length > 0 && (
<ul
className="form-control__box"
aria-labelledby="autocompleteMenu"
{...getListboxProps()}
>
{groupedOptions.map((option, index) => {
return (
<li
{...getOptionProps({ option, index })}
{...listItem}
onClick={(ev) => {
setSelectedItem(option.name);
getOptionProps({ option, index }).onClick(ev);
}}
>
<div>{option.name}</div>
</li>
);
})}
</ul>
)}
</div>
</div>
);
}
export default AutocompleteComponent;
You can modify the props passed to the inputbox, before applying them. That way you can delete the event that happens when the input is clicked.
var newProps = getInputProps();
delete newProps.onMouseDown; //delete the extra MouseDown event
return (
...
<input
...
placeholder="Location"
{...newProps} //use newProps rather than calling getInputProps
And it will stop showing that popup when you click it.
You can check the working code here
I have designed a component MyCheckBox ( i used it as helper component ), so I imported it in another component, but whenever i am trying to trigger the event, it not triggered.
Code for reference
MyCheck Box
interface MyCheckBoxProps
{
onClick?:MouseEventHandler<HTMLInputElement>;
checked?:boolean
}
export const MyCheckBox (props:MyCheckBoxProps): JSX.Element =>{
return(
<input
type="checkbox"
id={id}
onClick={onClick}
checked={checked}
/>
)
}
When I Imported it
const[check,setCheck] = useState(false)
const handleCheckChange = (e: any, key: string) => {
console.log("check", e) // => this console log does not show up
if (key === "newPostActivity") {
console.log("check", e)
setCheck(e.target.checked)
}
}
<MyCheckBox checked={check} onClick={(e)=>handleCheckChange(e,"some string")}/>
Whenever I click on MyCheckBox , the event ( handleCheckChange )is never triggered
Anybody please help?
you are calling
<MyComponent checked={check}
onClick={(e) => handleCheckChange(e, "some string")}/>
in your App component which is the parent component. Here your checked and onclick are the props. so now accessing the props in your <MyComponent you have to make sure to call it as
import React from "react";
export default function MyCheckBox(props) {
return (
<input type="checkbox" onClick={props.onClick} checked={props.checked} />
);
}
OR
destructrize your props like
import React from "react";
export default function MyCheckBox({onClick, checked}) {
return (
<input type="checkbox" onClick={onClick} checked={checked} />
);
}
I don't understand if you wrote this code as an example of a not working one. If this is the final code, you must destruct the props to use like that.
<input
type="checkbox"
id={id}
onClick={onClick}
checked={checked}
/>
Otherwise, you should call:
<input
type="checkbox"
id={props.id}
onClick={props.onClick}
checked={props.checked}
/>
I've rewrote your code:
App
export default function App() {
const [check, setCheck] = useState(false);
const handleCheckChange = (e: MouseEvent<HTMLInputElement>, key: string) => {
console.log("check", e);
setCheck(!check);
console.log("I am state", check);
};
return (
<div>
<MyCheckBox
id="foo"
checked={check}
onClick={(e) => handleCheckChange(e, "some string")}
/>
</div>
);
}
MyCheckBox
interface MyCheckBoxProps {
id: string;
onClick?: MouseEventHandler<HTMLInputElement>;
checked?: boolean;
}
const MyCheckBox: React.FC<MyCheckBoxProps> = (props) => {
const { id, onClick, checked } = props;
return <input type="checkbox" id={id} onClick={onClick} checked={checked} />;
};
export default MyCheckBox;
Copied from my codesandbox
I've a isView and setIsView in the ParentComponent and passing them down to the ChildComponent as props and trying to do show/hide conditional rendering but setIsView seems not to be working and isView value in the props remains same.
const ParentComponent = props => {
const [isView, setIsView] = useState(true);
const onChange = selectedOption => {
selectedOption === 'Report'
? setIsView(true)
: setIsView(false);
};
return (
<div>
<ChildComponent
isView={isView}
onChange={onChange}
/>
</div>
);
};
const ChildComponent = props => {
const {isView, onChange} = props;
return (
<div>
<RadioButton
onChange={() => onChange('Not-Report')}
/>
<If condition={isView}>
<ChildComponent2>
</If>
</div>
);
};
Edit: changed onChange={onChange('Not-Report')} to onChange={() => onChange('Not-Report')} as suggested by some. still not working.
Try feeding the onChange method as a callback function instead.
const ChildComponent = props => {
const {isView, onChange} = props;
return (
<div>
<RadioButton
onChange={() => onChange('Not-Report')} // <- Here
/>
<If condition={isView}>
<ChildComponent2>
</If>
</div>
);
};
Update child component onChange function as follows:
<RadioButton
onChange={() => onChange('Not-Report')}
/>
If you pass onChange only, it will be regarded with the function that has event as a parameter rather than the prop's onChange function.
To make it work like your way,
const ChildComponent = ({isView, onChange}) => {
const onRadioChange = () => {
onChange('Not-Report')}
}
return (
<div>
<RadioButton
onChange={onRadioChange}
/>
<If condition={isView}>
<ChildComponent2>
</If>
</div>
);
};
i am wrote this code
ParentComponent
const ParentComponent = (props) => {
const [isView, setIsView] = useState(true);
const onChange = (selectedOption) => {
console.log("selectedOption = ", selectedOption);
selectedOption === "Report" ? setIsView(true) : setIsView(false);
};
return (
<div>
<ChildComponent isView={isView} onChange={onChange} />
</div>
);
};
ChildComponent
const ChildComponent = (props) => {
const { isView, onChange } = props;
return (
<div>
<input
type="radio"
checked={isView}
onClick={() => {
onChange("Not-Report");
}}
/>
isView = {isView ? "true" : "false"}
</div>
);
};
i change onChange to onClick and use checked
Work Demo
const shoopingList = [{name:'some thing', id:1},{name:'some string', id:4}]
const CurrentLists = ({ shoppingList }) => {
const arr = [...shoppingList]
arr.map((item, index) => {
item.isChecked = false
})
const [checkedItems, setCheckeditems] = useState(arr)
const handleOnChange = (e) => {
const index = e.target.name
const val = e.target.checked
checkedItems[index].isChecked = e.target.checked
setCheckeditems([...checkedItems])
}
return (
<div>
{checkedItems.map((item, index) => {
console.log('item check', item.isChecked)
return (
<CheckBox
key={index}
name={index}
checked={item.isChecked}
text={item.name}
onChange={handleOnChange}
/>
)
})}
</div>
)
}
const CheckBox = ({ checked, onChange, text, className = '', name }) => {
let css = classnames({
activebox: checked,
})
return (
<div className={'CheckBoxComponent ' + className}>
<div className={'checkbox ' + css}>
<input
name={name}
type="checkbox"
onChange={onChange}
/>
{checked && <i className="far fa-check signcheck" />}
</div>
<label>{text}</label>
</div>
)
}
I got some checkboxes. when I click the checkbox, my component doesn't re-render. What's wrong here? I might be using the hook setState wrong.
On every re-render you are basically setting isChecked property to false. Try updating your component like this:
const CurrentLists = ({ shoppingList }) => {
const [checkedItems, setCheckeditems] = useState(shoppingList)
const handleOnChange = useCallback(
(e) => {
const index = e.target.name
let items = [...checkedItems];
items[index].isChecked = e.target.checked;
setCheckeditems(items);
}, [checkedItems]
);
return (
<div>
{checkedItems.map((item, index) => {
console.log('item check', item.isChecked)
return (
<CheckBox
key={index}
name={index}
checked={item.isChecked}
text={item.name}
onChange={handleOnChange}
/>
)
})}
</div>
)
}
You may also notice usage of useCallback. It ensures that your callback is memoized and not created on every re-render - more about it.
In handleOnChange you are mutating the state directly, and because the state reference is not changed React does not re-render. To fix this change the line setCheckeditems(checkedItems) to setCheckeditems([...checkedItems]).
Also in your render, you are rendering shoppingList, but what you need to render is checkedItems
Let say I have component FilterItem as
export const FilterItem = (props: Props) => {
const { value, action, checked, label, index } = props;
return (
<label
className={classnames(styles.filterItemLabel)}
>
<Checkbox
className={ classnames(styles.filterItemInput) }
checked={ checked }
onChange={ () => action(value, index) }
/>
{ label }
</label>
);
};
and Checkbox component as:
export const Checkbox = (props: Props) => {
const { checked, onChange, className, disabled } = props;
return (
<span className={styles['checkboxWrapper']}>
<input
style={{ display: 'none' }}
type="checkbox"
checked={checked}
onChange={onChange}
disabled={disabled}
/>
</span>
);
};
My question, is it possible to write FilterItem component as: putting onChange function inside label as props. for my case, it still working. dont know the reason. is it specific to React.