Related
Hi Im using this package, All working fine but I can't select a value by default programatically which is user selected already using same component .
https://codesandbox.io/s/github/mikepricedev/mui-tree-select
Developer saying use this doc https://mikepricedev.github.io/mui-tree-select/interfaces/TreeSelectProps.html#value we can set, But I cant understand. Pls help me fix it.
Here is a working example of how to use a default value. I have changed this source to have an initial value programatically. value: "2:0"
import React, { useCallback, useState } from "react";
import TreeSelect, { BranchNode, defaultInput } from "mui-tree-select";
const generateOptions = (parentBranch, randomAsync = true) => {
const depth = parentBranch
? Number.parseInt(parentBranch.valueOf().label.split(":")[0]) + 1
: 0;
const options = [];
for (let i = 0, len = Math.ceil(Math.random() * 10); i < len; i++) {
const option = `${depth}:${i}`;
options.push(new BranchNode({ label: option }, parentBranch), option);
}
return randomAsync && Math.random() > 0.5
? new Promise((resolve) => {
setTimeout(() => {
resolve(options);
}, Math.ceil(Math.random() * 1000));
})
: options;
};
const getOptionLabel = (option) =>
option instanceof BranchNode ? option.valueOf().label : option.toString();
const defaultBranch = BranchNode.createBranchNode([
{ label: "0:5" },
{ label: "1:2" }
]);
const Sample = () => {
const [state, setState] = useState({
single: {
value: "2:0",
options: generateOptions(defaultBranch, false),
loading: false,
branch: defaultBranch
},
multiple: {
value: [],
options: generateOptions(null, false),
loading: false,
branch: null
}
});
return (
<div style={{ width: 350, padding: 16 }}>
<TreeSelect
branch={state.single.branch}
onBranchChange={(_, branch) => {
const options = generateOptions(branch);
if (options instanceof Promise) {
setState((state) => ({
...state,
single: {
...state.single,
branch,
loading: true
}
}));
options.then((options) => {
setState((state) => ({
...state,
single: {
...state.single,
options,
loading: false
}
}));
});
} else {
setState((state) => ({
...state,
single: {
...state.single,
branch,
options,
loading: false
}
}));
}
}}
options={state.single.options}
loading={state.single.loading}
getOptionLabel={getOptionLabel}
renderInput={useCallback(
(params) =>
defaultInput({
...params,
variant: "outlined",
label: "Single"
}),
[]
)}
value={state.single.value}
onChange={useCallback(
(_, value) => {
setState((state) => ({
...state,
single: {
...state.single,
value
}
}));
},
[setState]
)}
/>
<div style={{ height: "16px" }} />
<TreeSelect
onBranchChange={(_, branchOption) => {
const options = generateOptions(branchOption);
if (options instanceof Promise) {
setState((state) => ({
...state,
multiple: {
...state.multiple,
loading: true
}
}));
options.then((options) => {
setState((state) => ({
...state,
multiple: {
...state.multiple,
options,
loading: false
}
}));
});
} else {
setState((state) => ({
...state,
multiple: {
...state.multiple,
options,
loading: false
}
}));
}
}}
options={state.multiple.options}
loading={state.multiple.loading}
getOptionLabel={getOptionLabel}
freeSolo
multiple
renderInput={useCallback(
(params) =>
defaultInput({
...params,
variant: "outlined",
label: "Multiple"
}),
[]
)}
/>
</div>
);
};
export default Sample;
Hi I have got solution, That you have to pass particular object with
const [value, setValue] = useState(null);
useEffect(() => {
if (defaultIndustries !== null) {
setValue(() => new Node(defaultIndustries));
}
}, [defaultIndustries]);
And
<TreeSelect
getChildren={(node) =>
syncOrAsync(
node
? node.getChildren()
: industryMultiTree.map((country) => new Node(country)),
runAsync
)
}
getOptionDisabled={(option) => {
var _a;
return (
option.isBranch() &&
!((_a = option.getChildren()) === null || _a === void 0
? void 0
: _a.length)
);
}}
getParent={(node) => syncOrAsync(node.getParent(), runAsync)}
isBranch={(node) => syncOrAsync(node.isBranch(), runAsync)}
isOptionEqualToValue={(option, value) => {
return option instanceof FreeSoloNode ? false : option.isEqual(value);
}}
value={value}
renderInput={(params) => (
<TextField
{...params}
label={label}
error={error}
helperText={helperText}
/>
)}
sx={{ mb: 3 }}
onChange={(_, value) => {
onChangeSelect(value);
}}
/>
Im using Antd library and i can't seem to find where i have the bug.
This is my EditableTableCell component
import React, {Component} from 'react';
import { Form } from '#ant-design/compatible';
import '#ant-design/compatible/assets/index.css';
import { Input, InputNumber, Select, DatePicker } from "antd";
import moment from "moment";
import {EditableContext} from "./EditableTableRow";
const FormItem = Form.Item;
const Option = Select.Option;
class EditableTableCell extends Component {
getInput = (record, dataIndex, title, getFieldDecorator) => {
switch (this.props.inputType) {
case "number":
return (
<FormItem style={{ margin: 0 }}>
{getFieldDecorator(dataIndex, {
rules: [
{
required: true,
message: `Please Input ${title}!`
}
],
initialValue: record[dataIndex]
})(
<InputNumber formatter={value => value} parser={value => value} />
)}
</FormItem>
);
case "date":
return (
<FormItem style={{ margin: 0 }}>
{getFieldDecorator(dataIndex, {
initialValue: moment(record[dataIndex], this.dateFormat)
})(<DatePicker format={this.dateFormat} />)}
</FormItem>
);
case "select":
return (
<FormItem style={{ margin: 0 }}>
{getFieldDecorator(dataIndex, {
initialValue: record[dataIndex]
})(
<Select style={{ width: 150 }}>
{[...Array(11).keys()]
.filter(x => x > 0)
.map(c => `Product ${c}`)
.map((p, index) => (
<Option value={p} key={index}>
{p}
</Option>
))}
</Select>
)}
</FormItem>
);
default:
return (
<FormItem style={{ margin: 0 }}>
{getFieldDecorator(dataIndex, {
rules: [
{
required: true,
message: `Please Input ${title}!`
}
],
initialValue: record[dataIndex]
})(<Input />)}
</FormItem>
);
}
}
render() {
const { editing, dataIndex, title, inputType, record, index,...restProps} = this.props;
return (
<EditableContext.Consumer>
{form => {
const { getFieldDecorator } = form;
return (
<td {...restProps}>
{editing ?
this.getInput(record, dataIndex, title, getFieldDecorator)
: restProps.children}
</td>
);
}}
</EditableContext.Consumer>
);
}
}
export default EditableTableCell;
This is my EditableTableCell component
import React, {Component} from 'react';
import { Form} from '#ant-design/compatible';
export const EditableContext = React.createContext();
class EditableTableRow extends Component {
render() {
return (
<EditableContext.Provider value={this.props.form}>
<tr {...this.props} />
</EditableContext.Provider>
);
}
}
export default EditableTableRow=Form.create()(EditableTableRow);
This is my ProductsPage component im having bug in
import React, {Component} from 'react';
import {Button, Layout, notification, Popconfirm, Space, Table,Typography} from "antd";
import {Link} from "react-router-dom";
import {Content} from "antd/es/layout/layout";
import EditableTableRow, {EditableContext} from "../components/EditableTableRow";
import EditableTableCell from "../components/EditableTableCell";
import API from "../server-apis/api";
import {employeesDataColumns} from "../tableColumnsData/employeesDataColumns";
import {CheckCircleFilled, InfoCircleFilled} from "#ant-design/icons";
class ProductsPage extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
error: null,
isLoaded: false,
editingKey: "",
errorMessage: "",
}
}
columns = [
...employeesDataColumns,
{
title: "Actions",
dataIndex: "actions",
width: "10%",
render: (text, record) => {
const editable = this.isEditing(record);
return editable ? (
<span>
<EditableContext.Consumer>
{form => (<a onClick={() => this.saveData(form, record.username)} style={{ marginRight: 8 }}>Save</a>)}
</EditableContext.Consumer>
<a onClick={this.cancel}>Cancel</a>
</span>
) : (
<Space size="middle">
<a onClick={() => this.edit(record.username)}>Edit</a>
<Popconfirm title="Are you sure you want to delete this product?"
onConfirm={() => this.remove(record.username)}>
<a style={{color:"red"}}>Delete</a>
</Popconfirm>
</Space>
);
},
}
];
isEditing = (record) => {
return record.username === this.state.editingKey;
};
edit(username) {
this.setState({editingKey:username});
}
cancel = () => {
this.setState({ editingKey: ""});
};
componentDidMount() {
this.setState({ loading: true });
const token="Bearer "+ JSON.parse(localStorage.getItem("token"));
API.get(`users/all`,{ headers: { Authorization: token}})
.then(res => {
// console.log(res.data._embedded.productList);
const employees = res.data._embedded.employeeInfoDtoList;
this.setState({loading: false,data:employees });
})
}
async remove(username) {
const token="Bearer "+ JSON.parse(localStorage.getItem("token"));
API.delete(`/users/${username}`,{ headers: { Authorization: token}})
.then(() => {
let updatedProducts = [...this.state.data].filter(i => i.username !== username);
this.setState({data: updatedProducts});
this.successfullyAdded("Employee is deleted. It wont have any access to the website anymore.")
}).catch(()=>this.errorHappend("Failed to delete"));
}
hasWhiteSpace(s) {
return /\s/g.test(s);
}
saveData(form,username) {
form.validateFields((error, row) => {
if (error) {
return;
}
const newData = [...this.state.data];
const index = newData.findIndex(item => username === item.username);
const item = newData[index];
newData.splice(index, 1, {
...item,
...row
});
const token="Bearer "+ JSON.parse(localStorage.getItem("token"));
const response = API.put(`/users/${username}/update`, row,{ headers: { Authorization: token}})
.then((response) => {
this.setState({ data: newData, editingKey: ""});
this.successfullyAdded("Empolyee info is updated")
})
.catch(error => {
this.setState({ errorMessage: error.message });
this.errorHappend("Failed to save changes.")
console.error('There was an error!', error);
});
});
}
successfullyAdded = (message) => {
notification.info({
message: `Notification`,
description:message,
placement:"bottomRight",
icon: <CheckCircleFilled style={{ color: '#0AC035' }} />
});
};
errorHappend = (error) => {
notification.info({
message: `Notification`,
description:
`There was an error! ${error}`,
placement:"bottomRight",
icon: <InfoCircleFilled style={{ color: '#f53333' }} />
});
};
render() {
const components = {
body: {
row: EditableTableRow,
cell: EditableTableCell
}
};
const columns = this.columns.map(col => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: record => {
const checkInput = index => {
switch (index) {
case "price":
return "number";
default:
return "text";
}
};
return {
record,
// inputType: col.dataIndex === "age" ? "number" : "text",
inputType: checkInput(col.dataIndex),
dataIndex: col.dataIndex,
title: col.title,
editing: this.isEditing(record)
};
}
};
});
const { data, loading } = this.state;
return (
<Layout>
<div>
<Link to="/add-product">
<Button style={{float:"right", background: "#0AC035",marginBottom:"1em", marginTop:"1em" }}
type="primary">New emplyee</Button>
</Link>
</div>
<Content>
<Table components={components} bordered dataSource={data} columns={columns} loading={loading} rowKey={data.username} rowClassName="editable-row"/>
</Content>
</Layout>
);
}
}
export default ProductsPage;
This is the bug I'm having:
enter image description here
And i want to have this result like its shown in Antd docs:
enter image description here
Id really appreciate if you take a look and help me figure out where im wrong
Updated Solution:
I find the issue. In render where you map the columns, you just return the column if it's not an editable column. You can check the code below. I added a check if it's dataIndex === 'actions', then return the following code:
Please Follow the link:
https://react-ts-v3fbst.stackblitz.io
Changes:
1.In columns, i remove the render function from the action object:
{
title: 'Actions',
dataIndex: 'actions',
width: '10%',
},
2. In render function where you map the columns, add the following code before this condition if(!col.editable) {,:
if (col.dataIndex === 'actions') {
return {
...col,
render: (text, record) => {
const editable = this.isEditing(record);
return editable ? (
<span>
<EditableContext.Consumer>
{(form) => (
<a onClick={() => this.saveData(form, record.username)} style={{ marginRight: 8 }}>
Save
</a>
)}
</EditableContext.Consumer>
<a onClick={this.cancel}>Cancel</a>
</span>
) : (
<Space size='middle'>
<a onClick={() => this.edit(record.username)}>Edit</a>
<Popconfirm title='Are you sure you want to delete this product?' onConfirm={() => this.remove(record.username)}>
<a style={{ color: 'red' }}>Delete</a>
</Popconfirm>
</Space>
);
}
};
}
When you click on edit, you set the username as key for that particular row for editing, make sure you have username in each record. I tested this using the following data:
const data = [
{ id: 8, name: 'baun', model: '2022', color: 'black', price: 358, quantity: 3, username: 'brvim' },
{ id: 3, name: 'galileo', model: '20221', color: 'white', price: 427, quantity: 7, username: 'john' }
];
Most important, you should select that attribute as key that is unique in all records. As you are using username, i don't know what is your business logic or data looks like, but technically each record can have same username. So you must select something that would always be unique in your complete data.
I define a const varible that contains table columns, which was needed to invoke the function in react component. This is my code snippet looks like:
const columns = [
{
title: 'ID',
dataIndex: 'id',
key: 'id'
},
{
title: 'edit',
key: 'action',
render: (text, record) => (
<span>
<Button type='link'>detail</Button>
<Divider type='vertical' />
<Button onClick={this.editApp} type='link'>edit</Button>
</span>
)
}
]
class App extends Component {
state = {
loading: false,
pageNum: 1,
pageSize: 10,
isAddModalVisible: false,
isEditModalVisible: true
}
enterLoading = () => {
this.setState({
loading: true
})
}
editApp = () => {
this.setState({
isAddModalVisible: true
})
}
}
when I run this code, tell me that:
TypeError: Cannot read properties of undefined (reading 'editApp')
What should I do to invoke this function in const columns? I tried to move the columns into the component but still did not work. This is my full code of this component:
import React, { Component } from 'react'
import CustomBreadcrumb from '#/components/CustomBreadcrumb'
import { Layout, Divider, Row, Col, Table, Button, notification, Form, message } from 'antd'
import '#/style/view-style/table.scss'
import { withRouter } from 'react-router-dom'
import { getAppList, addApp } from '#/service/global/AppService'
import moment from 'moment'
import AddApp from './crud/AddApp'
import EditApp from './crud/EditApp'
const columns = [
{
title: 'ID',
dataIndex: 'id',
key: 'id'
},
{
title: '应用名',
dataIndex: 'app_name',
key: 'app_name'
},
{
title: '应用编号',
dataIndex: 'app_id',
key: 'app_id'
},
{
title: '应用英文缩写',
dataIndex: 'app_abbr',
key: 'app_abbr'
},
{
title: '用户数',
dataIndex: 'user_count',
key: 'user_count'
},
{
title: '上线状态',
dataIndex: 'online_status',
key: 'online_status',
render: (text, record) => <span>{record.online_status === 1 ? '已上线' : '未上线'}</span>
},
{
title: '创建时间',
dataIndex: 'created_time',
key: 'created_time',
render: text => <span>{moment.unix(parseInt(text) / 1000).format('YYYY-MM-DD HH:mm:ss')}</span>
},
{
title: '备注',
dataIndex: 'remark',
key: 'remark'
},
{
title: '操作',
key: 'action',
render: (text, record) => (
<span>
<Button type='link'>详情</Button>
<Divider type='vertical' />
<Button onClick={this.editApp} type='link'>编辑</Button>
</span>
)
}
]
class App extends Component {
state = {
loading: false,
pageNum: 1,
pageSize: 10,
isAddModalVisible: false,
isEditModalVisible: true
}
enterLoading = () => {
this.setState({
loading: true
})
}
addApp = () => {
this.setState({
isAddModalVisible: true
})
}
editApp = () => {
this.setState({
isEditModalVisible: true
})
}
onPageChange = current => {
this.setState({
pageNum: current
})
let request = {
pageSize: this.state.pageSize,
pageNum: current
}
getAppList(request)
}
changePageSize(pageSize, current) {
this.setState({
pageSize: pageSize
})
let request = {
pageSize: pageSize,
pageNum: this.state.pageNum
}
getAppList(request)
}
onAddModalCancelClick = (rowData = {}) => {
const { isAddModalVisible } = this.state
this.setState({ isAddModalVisible: !isAddModalVisible })
}
onCreateApp = values => {
let params = {
appName: values.appName,
appAbbr: values.appAbbr
}
addApp(params)
}
componentDidMount() {
let request = {
pageSize: this.state.pageSize,
pageNum: this.state.pageNum
}
getAppList(request)
}
componentWillUnmount() {
notification.destroy()
this.timer && clearTimeout(this.timer)
}
render() {
let data = this.props.app.app.list
let apps = this.props.app.app
if ((data && Object.keys(data).length === 0) || data === undefined) {
return <div></div>
}
let total = parseInt(apps.pagination.total)
const paginationProps = {
showSizeChanger: true,
showQuickJumper: true,
pageSize: apps.pagination.pageSize,
pageSizeOptions: ['10', '20', '30'],
showTotal: () => `共${total}条`,
current: apps.pagination.pageNum,
total: total,
onShowSizeChange: (current, pageSize) => this.changePageSize(pageSize, current),
onChange: current => this.onPageChange(current)
}
return (
<Layout>
<div>
<CustomBreadcrumb arr={['应用', '全局', '应用']}></CustomBreadcrumb>
</div>
<Row>
<Col>
<div className='base-style'>
<h3 id='basic'>应用管理</h3>
<Divider />
<Button
type='primary'
onClick={this.addApp}
shape='round'
style={{ width: 90, marginRight: 8 }}>
添加应用
</Button>
<Table columns={columns} dataSource={data} pagination={paginationProps} rowKey='id' />
<AddApp
visible={this.state.isAddModalVisible}
onVisibleChange={this.onAddModalCancelClick}
onCreate={this.onCreateApp}
{...{ data }}
/>
<EditApp
visible={this.state.isEditModalVisible}
onVisibleChange={this.onAddModalCancelClick}
onCreate={this.onCreateApp}
{...{ data }}
/>
</div>
</Col>
</Row>
</Layout>
)
}
}
export default withRouter(App)
You can't access funtion outside its lexical scope
import React, { Component } from "react";
import CustomBreadcrumb from "#/components/CustomBreadcrumb";
import {
Layout,
Divider,
Row,
Col,
Table,
Button,
notification,
Form,
message,
} from "antd";
import "#/style/view-style/table.scss";
import { withRouter } from "react-router-dom";
import { getAppList, addApp } from "#/service/global/AppService";
import moment from "moment";
import AddApp from "./crud/AddApp";
import EditApp from "./crud/EditApp";
class App extends Component {
state = {
loading: false,
pageNum: 1,
pageSize: 10,
isAddModalVisible: false,
isEditModalVisible: true,
};
enterLoading = () => {
this.setState({
loading: true,
});
};
addApp = () => {
this.setState({
isAddModalVisible: true,
});
};
editApp = () => {
this.setState({
isEditModalVisible: true,
});
};
onPageChange = (current) => {
this.setState({
pageNum: current,
});
let request = {
pageSize: this.state.pageSize,
pageNum: current,
};
getAppList(request);
};
changePageSize(pageSize, current) {
this.setState({
pageSize: pageSize,
});
let request = {
pageSize: pageSize,
pageNum: this.state.pageNum,
};
getAppList(request);
}
onAddModalCancelClick = (rowData = {}) => {
const { isAddModalVisible } = this.state;
this.setState({ isAddModalVisible: !isAddModalVisible });
};
onCreateApp = (values) => {
let params = {
appName: values.appName,
appAbbr: values.appAbbr,
};
addApp(params);
};
componentDidMount() {
let request = {
pageSize: this.state.pageSize,
pageNum: this.state.pageNum,
};
getAppList(request);
}
componentWillUnmount() {
notification.destroy();
this.timer && clearTimeout(this.timer);
}
render() {
let data = this.props.app.app.list;
let apps = this.props.app.app;
if ((data && Object.keys(data).length === 0) || data === undefined) {
return <div></div>;
}
let total = parseInt(apps.pagination.total);
const paginationProps = {
showSizeChanger: true,
showQuickJumper: true,
pageSize: apps.pagination.pageSize,
pageSizeOptions: ["10", "20", "30"],
showTotal: () => `共${total}条`,
current: apps.pagination.pageNum,
total: total,
onShowSizeChange: (current, pageSize) =>
this.changePageSize(pageSize, current),
onChange: (current) => this.onPageChange(current),
};
const columns = [
{
title: "ID",
dataIndex: "id",
key: "id",
},
{
title: "应用名",
dataIndex: "app_name",
key: "app_name",
},
{
title: "应用编号",
dataIndex: "app_id",
key: "app_id",
},
{
title: "应用英文缩写",
dataIndex: "app_abbr",
key: "app_abbr",
},
{
title: "用户数",
dataIndex: "user_count",
key: "user_count",
},
{
title: "上线状态",
dataIndex: "online_status",
key: "online_status",
render: (text, record) => (
<span>{record.online_status === 1 ? "已上线" : "未上线"}</span>
),
},
{
title: "创建时间",
dataIndex: "created_time",
key: "created_time",
render: (text) => (
<span>
{moment.unix(parseInt(text) / 1000).format("YYYY-MM-DD HH:mm:ss")}
</span>
),
},
{
title: "备注",
dataIndex: "remark",
key: "remark",
},
{
title: "操作",
key: "action",
render: (text, record) => (
<span>
<Button type="link">详情</Button>
<Divider type="vertical" />
<Button onClick={this.editApp} type="link">
编辑
</Button>
</span>
),
},
];
return (
<Layout>
<div>
<CustomBreadcrumb arr={["应用", "全局", "应用"]}></CustomBreadcrumb>
</div>
<Row>
<Col>
<div className="base-style">
<h3 id="basic">应用管理</h3>
<Divider />
<Button
type="primary"
onClick={this.addApp}
shape="round"
style={{ width: 90, marginRight: 8 }}
>
添加应用
</Button>
<Table
columns={columns}
dataSource={data}
pagination={paginationProps}
rowKey="id"
/>
<AddApp
visible={this.state.isAddModalVisible}
onVisibleChange={this.onAddModalCancelClick}
onCreate={this.onCreateApp}
{...{ data }}
/>
<EditApp
visible={this.state.isEditModalVisible}
onVisibleChange={this.onAddModalCancelClick}
onCreate={this.onCreateApp}
{...{ data }}
/>
</div>
</Col>
</Row>
</Layout>
);
}
}
export default withRouter(App);
I am trying to conditionally disable the checkbox in react, based on the count. Passing the value through props whether it is checked and greater than the number. I am saving the name in the state to further process it to send to in the backend database.
Here is my react code.
class CheckboxComponent extends Component {
constructor(props) {
super(props);
this.state = {
checkedItems: {}
};
}
handleChange = (event, formKey) => {
const {checkedItems} = this.state;
const checkedValues = {...checkedItems};
checkedValues[event.target.name] = event.target.checked;
this.setState((prevState, currState) => {
return {
...prevState,
checkedItems: checkedValues
}
});
};
render = () => {
const {checkedItems} = this.state;
const checkedValues = {...checkedItems};
const checkedCount = Object.values(checkedValues).length;
const checked = Object.values(checkedValues);
const disabled = checkedCount >= 3;
return (
<div>
{checkboxes.map((item, index) => (
<label className={`form__field__input__label`} key={item.key}>
<Input
type={`checkbox`}
name={item.name}
checked={this.state.checkedItems[item.name] || false}
onChange={this.handleChange}
formKey={'subjects'}
disabled={(!checked[index] && checked.length > 3)}
/>
{item.name}
</label>
))}
</div>
)
This is the Array that I am passing to render the values in the checkbox
const checkboxes = [
{
name: "Math and economics",
key: "mathsandeconomics",
label: "Math and economics"
},
{
name: "Science",
key: "Science",
label: "Science"
},
The below code snippet will work fine for you. And you can sent object to the backend having maximum of only 3 properties set to true. Get the full code from codesandbox link https://codesandbox.io/s/emmeiwhite-0i8yh
import React from "react";
const checkboxes = [
{
name: "Math and economics",
key: "mathsandeconomics",
label: "Math and economics",
},
{
name: "Science",
key: "science",
label: "Science",
},
{
name: "history",
key: "history",
label: "history",
},
{
name: "literature",
key: "literature",
label: "literature",
},
];
class CheckboxComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
checkedItems: {},
count: 0,
};
}
handleChange = (event, formKey) => {
const { name, checked } = event.target;
const updatedCheckedItems = { ...this.state.checkedItems, [name]: checked };
this.setState({
checkedItems: updatedCheckedItems,
count: Object.values(updatedCheckedItems).filter((value) => value).length,
});
};
render = () => {
const checkedValues = { ...this.state.checkedItems };
const checkedCount = Object.values(checkedValues).filter((value) => value)
.length;
console.log(this.state.checkedItems);
return (
<div>
{checkboxes.map((item, index) => (
<label className={`form__field__input__label`} key={item.key}>
<input
type={`checkbox`}
name={item.name}
checked={this.state.checkedItems[item.name] || false}
onChange={this.handleChange}
disabled={!checkedValues[item.name] && checkedCount > 2}
/>
{item.name}
</label>
))}
</div>
);
};
}
export default CheckboxComponent;
Your checked.length counts all touched boxes, not checked only. If you uncheck an input, it still will be counted. Count only true, for example Object.values(checkedValues).filter(value => value).length.
Use names instead of indexes: disabled={!checkedValues[item.name] && checkedCount > 3}
You can see full solution here: https://codesandbox.io/s/confident-http-vlm04?file=/src/App.js
event.target.getAttribute('name');
try this to get name attribute, pretty sure event.target.name is 'undefined'
I see one use case is not taken care of. checkedCount should count the number of true values only.
const checkedCount = Object.values(checkedValues).length; // existing
const checkedCount = Object.values(checkedValues).filter(item=>item==true).length //replace with this line
This would solve the problem.
Here is the code and as well as codesandbox link
Codesandbox Link
import React from "react";
export class CheckboxComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
checkedItems: {},
checkedCount: 0
};
}
handleChange = (event, formKey) => {
const { checkedItems } = this.state;
const checkedValues = { ...checkedItems };
checkedValues[event.target.name] = event.target.checked;
this.setState((prevState, currState) => {
return {
...prevState,
checkedItems: checkedValues,
checkedCount: event.target.checked
? prevState.checkedCount + 1
: prevState.checkedCount - 1
};
});
};
render = () => {
const { checkboxes } = this.props;
const { checkedCount } = this.state;
const disabled = checkedCount >= 3;
return (
<div>
<p></p>
{checkboxes.map((item, index) => (
<label className={`form__field__input__label`} key={item.key}>
<input
type={`checkbox`}
name={item.name}
checked={this.state.checkedItems[item.name] || false}
onChange={this.handleChange}
disabled={!this.state.checkedItems[item.name] ? disabled : false}
/>
{item.name}
</label>
))}
</div>
);
};
}
My purpouse here is to create a group of checboxes. "Search everywhere" is default checked, if you check something else "Search everywhere" automatically unchecked, you can check as many different checkboxes as you want, until you check "search everywhere" again, if you do that all other checkboxes will unchecked.
I want to create it in function component with Hooks in React.
View: Image how it looks in browser
Everything is ready, but I stuck a little bit with toggle between one checkbox and group of checkboxes. I've tried useState and useEffect to controll useState callback. Thanks for help.
const ButtonCategory = (props) => {
const [state, setState] = useState({
normalCheckbox: false,
specialCheckbox: true
});
const { id, name, special, products } = props;
const toggleOthers = () => {
if (state.specialCheckbox) {
setState({
...state,
normalCheckbox: false // ofc its bad
});
} else if (state.normalCheckbox) {
setState({
...state,
specialCheckbox: false // ofc its bad
});
}
};
const toggleNormal = () => {
setState({
...state,
normalCheckbox: !state.normalCheckbox
});
};
const toggleSpecial = () => {
setState({
...state,
specialCheckbox: !state.specialCheckbox
});
};
useEffect(() => {
toggleOthers();
}, [state.specialCheckbox, state.normalCheckbox]);
return (
<>
<Label>
<StyledInput
type="checkbox"
id={id}
checked={special ? state.specialCheckbox : state.normalCheckbox}
onChange={special ? () => toggleSpecial() : () => toggleNormal()}
onClick={(e) => {
/* do something */
}}
/>{" "}
<div>
{" "}
{name} {special ? null : `(${products})`}
</div>
</Label>
</>
);
};
I believe you want something like this:
import React, { useState } from "react";
export const Checkboxes = () => {
const [checkedIds, setCheckedIds] = useState(new Set(["everywhere"]));
const handleCheck = ({ id, checked }) => {
if (checked) {
if (id === "everywhere") {
checkedIds.clear();
} else {
checkedIds.delete("everywhere");
}
checkedIds.add(id);
} else {
checkedIds.delete(id);
}
setCheckedIds(new Set(checkedIds));
};
return (
<form>
<label>
<input
id="everywhere"
type="checkbox"
checked={checkedIds.has("everywhere")}
onChange={(e) => handleCheck(e.target)}
/>{" "}
Search everywhere
</label>
<label>
<input
id="option-1"
type="checkbox"
checked={checkedIds.has("option-1")}
onChange={(e) => handleCheck(e.target)}
/>{" "}
Option 1
</label>
<label>
<input
id="option-2"
type="checkbox"
checked={checkedIds.has("option-2")}
onChange={(e) => handleCheck(e.target)}
/>{" "}
Option 2
</label>
</form>
);
};
Test case at codesandbox.io
May be this could be helpful
import React from "react";
import "./style.css";
export const App = () => {
const _checkboxes = [
{
id: "id1",
name: "111",
value: "111",
label: "111",
checked: true
},
{
id: "id2",
name: "222",
value: "222",
label: "222",
checked: false
},
{
id: "id3",
name: "333",
value: "333",
label: "333",
checked: false
}
];
const [checkboxes, setCheckboxes] = React.useState(_checkboxes);
const handleChange = id => e => {
setCheckboxes(checkboxes => {
const firstId = "id1";
const temp = checkboxes.map(c => {
if (firstId === id) {
c.checked = c.id === firstId ? !c.checked : false;
} else {
if (c.id === id) {
c.checked = !c.checked;
} else {
if (c.id === firstId) {
c.checked = false;
}
}
}
return c;
});
return [...temp];
});
};
return (
<div>
{checkboxes.map(checkbox => (
<div key={checkbox.id}>
<input
type="checkbox"
onChange={handleChange(checkbox.id)}
value={checkbox.value}
name={checkbox.name}
id={checkbox.id}
checked={checkbox.checked}
/>
<label htmlFor={checkbox.id}>{checkbox.label}</label>
</div>
))}
</div>
);
};
https://stackblitz.com/edit/react-rtxxfp?file=src%2FApp.js