I am trying to implement a triple-state checkbox to react. The repeated clicking on the checkbox would cycle through blank->checked->crossed->blank->...
like this. With all look around and searching for answers, I came along a reference: Indeterminate checkbox in React JSX.
I tried something like this:
export default function App() {
const [state, setState] = React.useState({
data: [
{
id: "1",
one: 0
},
{
id: "2",
one: 0
}
]
});
const indetSetter = React.useCallback(
(el, id) => {
if (el) {
if (el.id === id && state.data.map((datum) => datum.one === 2)) {
el.indeterminate = true;
}
}
},
[state]
);
const advance = (id, e) => {
setState((state) => {
state.data.map((datum) =>
datum.id === id ? { ...datum, one: (datum.one + 1) % 3 } : { ...datum }
);
});
return (
<>
{state.data.map((item) => {
// console.log("show", item);
return (
<input
key={item.id}
id={item.id}
type="checkbox"
checked={item.one === 1}
ref={(el) => indetSetter(el, item.id)}
onChange={(e) => advance(item.id, e)}
/>
);
})}
</>
);
}
But eventually it throws me an error: can't access property "indeterminate", el is null
or can't access property "data", state is undefined
.
Working snippet: https://codesandbox.io/s/relaxed-hill-qqb4y
Any help to resolve the same is highly appretiated.
Thanks in advance :)
As I commented, you need to encapsulate the indeterminate logic in a separate component to be able to update the dom when value changes via useEffect
as explained here
This is the component for a single indeterminate checkbox
IndeterminateInput.js
import React, { useCallback, useEffect, useRef } from 'react';
export default function IndeterminateInput({ id, name, value, onChange }) {
const el = useRef(null);
const onChangeHandler = useCallback(
event => {
onChange({ id, name, value: (value + 1) % 3 });
},
[id, name, value, onChange],
);
useEffect(() => {
el.current.checked = value === 1;
el.current.indeterminate = value === 2;
}, [value]);
return (
<input type="checkbox" name={name} ref={el} onChange={onChangeHandler} />
);
}
And then you render one IndeterminateInput
for each item in your main component passing the necessary props
export default function App() {
const [state, setState] = ...
const onChange = useCallback((item) => {
const { id, value } = item;
setState((prevState) => {
return {
data: prevState.data.map((datum) => {
return id === datum.id
? {
...datum,
one: value
}
: datum;
})
};
});
}, []);
return (
<>
{state.data.map((item, i) => {
return (
<IndeterminateInput
key={item.id}
id={item.id}
name={item.name}
value={item.one}
onChange={onChange}
/>
);
})}
</>
);
}
you can see it working in here: https://codesandbox.io/s/stoic-euler-e97so?file=/src/App.js