Checkbox Default Checked in React table is not working - javascript

I want to mark the checkbox checked inside a subComponent with the help of forwardref but i am not getting the result.
I have tried defaultChecked = {true} defaultValue = {true} inside input field but didn't succeed.
Here is the checkbox component
import { forwardRef, useEffect, useRef } from "react";
export const SubRowsCheckBox = forwardRef(({ indeterminate, ...rest }, ref) => {
const defaultRef = useRef();
const resolvedRef = ref || defaultRef;
useEffect(() => {
resolvedRef.current.defaultChecked = true
resolvedRef.current.indeterminate = indeterminate;
}, [resolvedRef, indeterminate]);
return (
<>
<div class="flex items-center">
<input
type="checkbox"
ref={resolvedRef}
{...rest}
id="A3-yes"
name="A3-confirmation"
class="opacity-0 absolute h-8 w-8"
/>
</div>
</>
);
});
This is how I called the checkbox Component.
= useTable(
{
columns,
data,
state : {expanded},
},
useExpanded,
useRowSelect,
(hooks) => {
hooks.visibleColumns.push((columns) => {
return [
...columns,
{
Header: "Choose Items",
id: "selection",
Cell: ({ row }) => (
(details.isSelected) ? (
<div>
<SubRowsCheckBox {...row.getToggleRowSelectedProps() } />
</div>
) : ( null
)
),
},
];
});
}
)
The component is rendered only if row has got some subRows.
I have also tried resolvedRef.current.checked = true. It marks the checkbox checked but it doesn't works for the all rows. Here are the results
These are the results of resolvedRef.current.checked = true. The defaultChecked prop isn't changing anything.
Any kind of help will be highly appreciated.
I want to mark all the subrows checkbox checked for the first render and rest of it works fine.

*REVISED ANSWER
React Table v7 has a lot of nice configurations. You can handle the initialState of your rows and subrows next to where you pass in columns and data.
To prevent unchecking boxes when deselecting a row, you'll need to set the autoResetHiddenColumns flag to `false.
To set some initial row and sub-rows as checked, you'll need to use the initialState object. It contains some optional properties: expanded and selectedRowIds. You'll notice the child-rows have decimal values for their keys.
{
columns,
data,
autoResetHiddenColumns: false,
initialState: {
expanded: {
0: true
},
selectedRowIds: {
0: true,
"0.0": true,
0.1: true,
0.2: true
}
}
},
https://react-table-v7.tanstack.com/docs/api/useRowState#table-options
Working Sandbox (revised)
https://codesandbox.io/s/react-table-userowselect-useexpanded-userowselect-issue-forked-pq4ewb
Now you should see the 1st row and it's 3 child-rows selected by default. When you expand the 2nd row and check the row-box, all 3 child-rows are expanded - the 1st row and it's 3 child-rows should remain unaffected.
I hope this helps!

Related

How to apply a filter only if one of the checkboxes is clicked using react and typescript?

i want to apply filter only when one of the checkboxes is clicked using react and typescript?
what i am trying to do?
there is a table with some data and a filter icon on the page. when user clicks the filter icon he sees two checkboxes namely "source on", "source off".
now initially when no checkbox clicked the table shows all data meaning data with both source on and source off like point 4 below.
when user clicks source on checkbox only then table shows data with source on
when user clicks source off checkbox only then table shows data with source off
when user clicks both the checkboxes table shows all the data with source on and source off like point 1.
now the query to backend should be send like so ?source=true if checkbox with source on clicked. ?source=false sent with source off clicked.
now if user clicks both checkboxes or doesnt click both checkboxes no filtering should be applied.
Here the table is using react-table.
below is the code,
const sourceFilterOptions = [
{ value: 'true', id: 'on', label: 'Source On'},
{ value: 'false', id: 'off', label: 'Source Off'}
]
const filterFields: any = [
{
name: 'source',
type: 'checkbox',
defaultValue: '' //not sure what to be set initially when no checkboxes
// clicked
label: 'Source',
options: sourceFilterOptions,
}
]
// this is the graphql query
query fetchData(
$source: String //not sure what type should be given
) {
some(
source: $source
) {
//some data
}
}
const MainComponent: React.FC<SomeProps> = ({}) => {
const filtersValues = useFiltersValues({filterFields});
const variables = React.useMemo(
() => ({
...queryOptions.variables,
...filtersValues,
}),
[queryOptions.variables, filtersValues]
);
const { data, loading, error } = fetchDataQuery({
...queryOptions,
variables,
});
const { tableData } = React.useMemo(
() => ({
tableData: data || [],
}),
[data]
);
return(
<TableWithFilters filterFields={filterFields}>
<Table
data={tableData}
columns={columns}
/>
</TableWithFilters>
);
}
const TableWithFilters: React.FC<TableWithFiltersProps> = ({
filterFields,
...props
}) => {
const {
isOpen: filtersIsOpen,
isActive: filtersIsActive,
toggleIsOpen: toggleFiltersIsOpen,
handleSubmit: filtersHandleSubmit,
defaultValues: filtersDefaultValues,
initialValues: filtersInitialValues,
} = useFilters({ filterFields, path, toggleState });
return (
<Formik
initialValues={filtersInitialValues}
onSubmit={filtersHandleSubmit}
>
{formikProps => {
const filtersIsDefault = isEqual(
formikProps.values,
filtersDefaultValues
);
const filters = (
<Filters
fields={filterFields}
isDefault={filtersIsDefault}
onResetClick={filtersOnReset}
{...formikProps}
/>
);
return (
<Main
leftPanelOpen={filtersIsOpen}
onLeftPanelCloseClick={toggleFiltersIsOpen}
leftPanelContent={filters}
{...props}
>
</Main>
);
}}
</Formik>
);};
As seen from above code i have set two checkboxes and passing the default Values for checkboxes (initially they are unchecked)
now the question is i dont know how to set the initial values and default values to the checkbox meaning such that i send either true or false to query.
if none selected no filters applied and if both selected no filters applied.
could someone help me with this as how to do it. thanks.
I recommend coding in a way that, when a user checks one the other option becomes unchecked automatically. You can attach refs to the checkboxes and manipulate their checked value accordingly. Like this:
const checkBox1 = useRef(null);
const checkBox2 = useRef(null);
render(
<>
<input
type="checkbox"
ref={checkBox1}
onChange={(e)=>{
if(e.target.checked) checkBox2.current.checked = false;
}}
/>
<input
type="checkbox"
ref={checkBox2}
onChange={(e)=>{
if(e.target.checked) checkBox1.current.checked = false;
}}
/>
</>
)

How to work with multiple checkboxes in react and collect the checked checkboxes

I'm currently working on a filter component where multiple checkboxes can be selected.
Now I want to toggle the state of the checkboxes (which is currently possible and works) and store the checked checkboxes in an array.
If a checkbox is unchecked, it should of course be removed from the array. I've tried the useState hook, but without success --> The checkboxes are added multiple times and the unchecked ones are not removed..
Here is the current status:
// Here I'm creating an array with a fixed size (based on the received data)
const [checkboxState, setCheckboxState] = useState(new Array(receivedData.length).fill(false));
// With this useState I wan't to collect the checked checkboxes
const [checkedCheckboxes, setCheckedCheckboxes] = useState([]);
// This is my handler method that gets triggered when a checkbox get's checked/unchecked
// ..and toggles the state of the checkbox
const handleCheckboxState = (position: number) => {
const updatedCheckedState = checkboxState.map((item, index) => (index === position ? !item : item));
setCheckboxState(updatedCheckedState);
collectCheckedCheckboxes();
};
// With this method I wan't to push the checked checkboxes into the array
// ..and remove the unchecked ones
const collectCheckedCheckboxes = () => {
checkboxState.map((item, index) => {
if (item === true) {
return checkedCheckboxes.push(receivedData[index]);
} else {
return checkedCheckboxes.slice(index, 1);
}
});
};
The checkboxes are rendered like this:
<div className="checkboxes">
{receivedData?.map((data, index) => (
<CheckBox
value={data.value}
checked={checkboxState[index]}
onChange={() => handleCheckboxState(index)}
/>
))}
</div>
What am I doing wrong?
Your CheckBox-component does not contain a key property. This is helpful for React to identify which items have changed, are added, or are removed.
Source: https://reactjs.org/docs/lists-and-keys.html
I also do not understand why you have two states, checkboxState and checkedCheckboxes. Is there another reason for this? I think this would be easier with a single state which holds the indexes (or values) of the checked checkboxes.
[update after comments]
The code below is the desired solution by OP to have the selected object values in a React state.
const { useState } = React;
const Checkboxes = () => {
// With this useState I wan't to collect the checked checkboxes
const [checkedCheckboxes, setCheckedCheckboxes] = useState([]);
// This is my handler method that gets triggered when a checkbox get's checked/unchecked
// ..and toggles the state of the checkbox
const handleCheckboxChange = (data) => {
const isChecked = checkedCheckboxes.some(checkedCheckbox => checkedCheckbox.value === data.value)
if (isChecked) {
setCheckedCheckboxes(
checkedCheckboxes.filter(
(checkedCheckbox) => checkedCheckbox.value !== data.value
)
);
} else {
setCheckedCheckboxes(checkedCheckboxes.concat(data));
}
};
const receivedData = [{ value: "A" }, { value: "B" }, { value: "C" }];
return (
<>
<div className="checkboxes">
<h1>Checkboxes:</h1>
{receivedData?.map((data, index) => (
<input
key={`cb-${index}`}
value={data.value}
type="checkbox"
checked={checkedCheckboxes.some(checkedCheckbox => checkedCheckbox.value === data.value)}
onChange={() => handleCheckboxChange(data)}
/>
))}
</div>
<div>
<h1>State:</h1>
<pre>{JSON.stringify(checkedCheckboxes, null, 2)}</pre>
</div>
</>
);
};
ReactDOM.render(<Checkboxes />, document.getElementById("app"));

Checkbox state gets empty after page change

I have an array of objects that looks like this:
const columns = [
{
key: "Source_campname",
title: "TS Camp Name",
customElement: function (row) {
return (
<FormControlLabel
control={
<Checkbox
checked={checkbox[row.id]}
key={row.id}
onChange={() =>
handleChange(row.Source_campname, row.id, checkbox)
}
name={row.id}
/>
}
label={[row.Source_campname]}
/>
);
}
},
{
key: "Tracker_campname",
title: "TR Camp Name"
}
];
You can see a "handleChange" function above, this is used to check/uncheck the component
The handleChange function looks like this:
const handleChange = (name, campid) => {
setCheckBox({ ...checkbox, [campid]: !checkbox[campid] });
};
You can also see a "customElement" function above. This function is rendered in another React component named ThanosTable. I will just write down part of the code where the rendering of customElement happens below.
return (
<> columnArray[0].customElement(row) </>
);
In the end you get 10 checkboxes, and you have a few pages that can be changed using pagination.
Do check my codesandbox link here for a working example:
https://codesandbox.io/s/magical-germain-8tclq
Now I have two problems:
Problem 1) If I select a few checkboxes, then go to second page and return, the checkbox state is empty and the original checkboxes are unselected. No idea why that is happening. How do I prevent that?
Problem 2) The value of checkbox state is always an empty object ({}) inside customElement function. You can see this by checking console.log(checkbox) inside customElement function (Check Line 76 in codesandbox). I thought it should be an array with selected checkbox items.
The useEffect hook embodies all the lifecycle events of a component. Therefore if you try to set checkbox in useEffect it'll infinitely update the component because updating state calls useEffect. This is probably why you see your state constantly being reset.
Instead, initialize your state with the rows before rendering.
const rows = [
...
];
let checkboxObj = {};
// if (rows) {
rows.forEach((e) => {
checkboxObj[e.id] = false;
});
const [checkbox, setCheckBox] = useState(checkboxObj);

Make only one row selectable at a time in React Table 7.1.0

I am trying to implement react table with just one row selectable at at time. I have gone through a lot of examples for multiple rows selection in a react table but in my case, the user can select only one row when the user clicks on the radio button but currently all the rows can be selected. Could anyone help me out on this to implement ?
I know this is an old question, but maybe someone will find this solution useful. Since version 7, react-table provides a stateReducer that can be used to track and change the state of a table. (before v7 it had reducerHandlers, but I didn't go deep into that). You can modify the state as follows:
useTable(
{
columns,
data,
stateReducer: (newState, action) => {
if (action.type === "toggleRowSelected") {
newState.selectedRowIds = {
[action.id]: true
}
}
return newState;
},
}
...
Here is the CodeSandbox with the changes described
Using react-table 7.5.0, I've put together a CodeSandbox with a react-table that functionally makes only one row selectable at a time.
In essence, I replaced an unconditionally rendered checkbox:
Cell: ({ row }) => (
<div>
<IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />
</div>
)
with a conditionally rendered checkbox:
Cell: ({ row }) => {
if (
rows.filter((row) => row.isSelected).length < 1 ||
row.isSelected
) {
return (
<div>
<IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />
</div>
);
} else {
return (
<div>
<IndeterminateCheckbox
checked={false}
readOnly
style={row.getToggleRowSelectedProps().style}
/>
</div>
);
}
}
I filtered all row objects to check for selected rows and then I conditionally rendered a normally functioning react-table checkbox if the number of selected rows is less than 1 or if the row is already selected.
If the number of checked rows is at least one and the row isn't selected, I render a read only checkbox that can't be selected.
Ideally, I would have liked to use react-table's built-in selectedRowIds instead of filtering through all row objects, but I couldn't figure out how to implement useTable() in a manner that allows me to refer to it since it is derived from it.
Here is react-table's Row Selection CodeSandbox that I forked from. Here's the relevant page in their docs.
I'll move the code into a built-in code snippet at a later time.
Cell: ({row}) => (
<IndeterminateCheckbox
{...row.getToggleRowSelectedProps({
onChange: () => {
const selected = row.isSelected; // get selected status of current row.
toggleAllRowsSelected(false); // deselect all.
row.toggleRowSelected(!selected); // reverse selected status of current row.
},
})}
/>
)
I've notice that #Ann answers works great with minor issue - if you want to toggle off selected row, it won't work.
I've added a validation to fix this:
useTable(
{
columns,
data,
stateReducer: (state, action) => {
if (action.type === 'toggleRowSelected' && Object.keys(state.selectedRowIds).length) {
const newState = { ...state };
newState.selectedRowIds = {
[action.id]: true,
};
return newState;
}
return state;
},
}
...
)
This is an old one, but here is my solution:
Get toggleAllRowsSelected from table instance.
const {
allColumns,
getTableBodyProps,
getTableProps,
headerGroups,
prepareRow,
rows,
state,
toggleAllRowsSelected,
} = useTable(
{
columns,
data,
},
useRowSelect
);
Then add onClick like in code below to your tr-component (in my code tr has been styled with emotion and named StyledTableRow). This will first set all selected rows to false and then toggle current row isSelected value if it was false initially, if it was true then it has already been set to false.
If you don't want to allow clicking selected row to unselect it (eg. for radio buttons), just use the isSelected === true to block any action here.
{rows.map((row, i) => {
prepareRow(row);
// const isRowSelected = isSelected(row.id);
const { isSelected, getRowProps, getToggleRowSelectedProps, toggleRowSelected } = row;
const {
onChange,
indeterminate,
...toggleRowSelectedProps
} = getToggleRowSelectedProps();
console.log(row);
return (
<StyledTableRow
hover
{...getRowProps()}
{...toggleRowSelectedProps}
selected={isSelected}
onClick={() => {
const current = isSelected;
toggleAllRowsSelected(false);
if (!current) {
toggleRowSelected();
}
}}
>
{row.cells.map((cell, key) => (
This is a simple react implementation of "radio-like" behaviour with useReducer to demonstrate how to use state management with table.
const { useReducer } = React; // --> for inline use
// import React, { useReducer } from 'react'; // --> for real project
const reducer = (state, action) => {
return { checkedId: action.id }
}
const App = () => {
const [state, dispatch] = useReducer(reducer, {})
const CheckBox = ({id}) => (
<input
id={id}
onClick={() => dispatch({ id })}
checked={state.checkedId === id}
type="checkbox"
/>
)
return (
<table border="1">
<tr><td><CheckBox id="1" /></td><td>John</td></tr>
<tr><td><CheckBox id="2" /></td><td>Doe</td></tr>
</table>
)
};
ReactDOM.render(<App />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.9.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.9.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
You can also override the onChange method on the Cell(...) prop function.
https://react-table.tanstack.com/docs/api/useRowSelect#row-properties
getToggleRowSelectedProps: Function(props) => props
Use this function to get the props needed for a select row checkbox.
Props:
onChange: Function()
style.cursor: 'pointer'
checked: Bool
title: 'Toggle Row Selected'
I'll give a sandbox later when I have the extra time.

How to use checkbox to achieve multiple selection and single selection?

Through the official documentation of antd we can know how to use the checkbox to complete the switch between multiple selection and single selection.
https://ant.design/components/checkbox/
My question is, if my checkbox data comes from a backend service, how should I maintain my data? It's accurate to say when I save the data in state of class so that the changes to the UI can be affected by changes in the data like the official documentation.
Now I try to traverse the back-end data when rendering the Dom, the following example code:
import { Checkbox } from 'antd';
const CheckboxGroup = Checkbox.Group;
class App extends React.Component {
state = {
indeterminate: true,
checkAll: false,
};
render() {
return (
<div>
<div style={{ borderBottom: '1px solid #E9E9E9' }}>
<Checkbox
indeterminate={this.state.indeterminate}
onChange={this.onCheckAllChange}
checked={this.state.checkAll}
>
Check all
</Checkbox>
</div>
<br />
{
this.renderDomFunction(data)
}
</div>
);
}
// data is from back-end server
renderDomFunction = (data) => {
let plainOptions = []
let defaultCheckedList = []
let dom
data.map(item => {
plainOptions.push(
{
label: <div>this is Orange</div>,
value: 'Orange',
disabled: false
},
{
label: <div>this is Apple</div>,
value: 'Apple',
disabled: false
},
)
defaultCheckedList.push('Orange','Apple')
})
return (
dom = <li>
<CheckboxGroup
options={plainOptions}
value={defaultCheckedList}
onChange={this.onChange}
/>
</li>
)
}
onChange = () => {
// code...
// I can't change the state of the checkbox by changing the data now, because isn't maintained in the state of Class.
}
}
ReactDOM.render(<App />, mountNode);
I also tried to put the setstate() function into the renderDomFunction but this would cause an infinite loop.
Thank you!

Categories