I am wanting to use the "Cascader" component of "Ant Design" but I am having trouble filling it with data. This is my code, which I am doing wrong, sorry I am still a newbie and I need your support please.
function CascaderEmpCliUn(props) {
const optionLists = { a: []}
const [state, setState] = useState(optionLists);
useEffect(() => {
async function asyncFunction(){
const empresas = await props.loginReducer.data.empresas;
const options = [
empresas.map(empresa => ({
value: empresa.id,
label: empresa.siglas,
children: [
empresa.cli_perm.map(cliente => ({
value: cliente.id,
label: cliente.siglas,
children: [
cliente.uunn_perm.map(un => ({
value: un.id,
label: un.nombre,
}))
]
}))
]})
)
];
setState({a : options})
}
asyncFunction();
}, [])
return (
<Cascader options={state.a} placeholder="Please select" />
)
}
ERROR
Not found value in options
I was able to reproduce your error with dummy data whenever I had an empty array of children at any level. I'm not sure why this should be a problem, but it is. So you need to modify your mapping function to check the length of the child arrays. It seems to be fine if passing undefined instead of an empty array if there are no children.
General Suggestions
You don't need to store the options in component state when you are getting them from redux. It can just be a derived variable. You can use useMemo to prevent unnecessary recalculation.
You are passing the entire loginReducer state in your props which is not ideal because it could cause useless re-renders if values change that you aren't actually using. So you want to minimize the amount of data that you select from redux. Just select the empresas.
Revised Code
function CascaderEmpCliUn() {
// you could do this with connect instead
const empresas = useSelector(
(state) => state.loginReducer.data?.empresas || []
);
// mapping the data to options
const options = React.useMemo(() => {
return empresas.map((empresa) => ({
value: empresa.id,
label: empresa.siglas,
children:
empresa.cli_perm.length === 0
? undefined
: empresa.cli_perm.map((cliente) => ({
value: cliente.id,
label: cliente.siglas,
children:
cliente.uunn_perm.length === 0
? undefined
: cliente.uunn_perm.map((un) => ({
value: un.id,
label: un.nombre
}))
}))
}));
}, [empresas]);
return <Cascader options={options} placeholder="Please select" />;
}
The final code of "options" object:
const options = useMemo(() => {
return empresas.map((empresa) => ({
value: empresa.id,
label: empresa.siglas,
children:
empresa.cli_perm.length === 0
? console.log("undefined")
:
empresa.cli_perm.map((cliente) => ({
value: cliente.id,
label: cliente.siglas,
children:
cliente.uunn_perm.length === 0
? console.log("undefined")
:
cliente.uunn_perm.map((un) => ({
value: un.id,
label: un.nombre
}))
}))
}));
}, [empresas]);
Related
since DataGrid follows react-table, I try to show subRows in DataGrid like this exmple but the subRows or getLeafRows array properties array in each row of Table stays empty array[]. I think there is a problem in DataGrid component!
my code is:
const items= useMemo(
() =>
data?.map((item) => ({
id: item.id,
name: item.name,
time: item.time,
subRows: parseStringToArray(data.children).map((child) => ({
id: child.id,
name: child.name,
time: child.time,
})),
})),
[data]
);
const onRow = useCallback<(row: Row<any>) => HTMLAttributes<HTMLTableRowElement>>((row) => {
return row.getCanExpand()
? {
onClick: row.getToggleExpandedHandler(),
style: { cursor: 'pointer' },
}
: {};
}, []);
const getRowCanExpand = useCallback<(row: Row<any>) => boolean>((row) => !!row.original.subRows?.length, []);
return(
<DataGrid
data: items || [],
columns: gridCols || [],
withRowExpanding: true,
getRowCanExpand,
renderSubComponent,
onRow,
withColumnFilters
withSorting
withFixedHeader
withPagination
highlightOnHover
height={1000}
pageSizes={['50', '100', '200']}
/>
)
I'll be happy if you help me in this concept.
I expect to show my subRows in datagrid like another rows in datagrid
Try this
const items = useMemo(
() =>
data?.map((item) => ({
id: item.id,
name: item.name,
time: item.time,
subRows: parseStringToArray(item.children)?.map((child) => ({
id: child.id,
name: child.name,
time: child.time,
})) || [],
})),
[data]
);
const onRow = useCallback<(row: Row<any>) => HTMLAttributes<HTMLTableRowElement>>((row) => {
return row.getCanExpand()
? {
onClick: row.getToggleExpandedHandler(),
style: { cursor: 'pointer' },
}
: {};
}, []);
const getRowCanExpand = useCallback<(row: Row<any>) => boolean>((row) => {
return !!row.original.subRows?.length;
}, []);
return (
<DataGrid
data={items || []}
columns={gridCols || []}
withRowExpanding={true}
getRowCanExpand={getRowCanExpand}
renderSubComponent={renderSubComponent}
onRow={onRow}
withColumnFilters={true}
withSorting={true}
withFixedHeader={true}
withPagination={true}
highlightOnHover={true}
height={1000}
pageSizes={['50', '100', '200']}
/>
);
In the useMemo hook, I changed the data.children property to item.children. This is because the data array does not have a children property, but each item in the array does.
I added a null check to the parseStringToArray function in case it returns null or undefined.
I added a default value of an empty array for the subRows property in case the parseStringToArray function returns null or undefined.
In the getRowCanExpand function, I added a null check for the subRows property using the optional chaining operator (?.). This is because the subRows property may be null or undefined if the parseStringToArray function returns null or undefined.
when i try to change the value in autocomplete of material-ui, i always get its value 0, here i have uploaded my whole code, can anyone please check my code and help me to resolve this issue ?
any help will be really appreciated.
export default function CreatePIC() {
const classes = useStyles();
const Department_list = [
{ label: 'Department1', id: 1 },
{ label: 'Department2', id: 2 },
{ label: 'Department3', id: 3 },
{ label: 'Department4', id: 4},
{ label: 'Department5', id: 5 }
]
const [department, setDepartment] = React.useState('');
const handleChangeDepartment = (event) => {
console.log(event.target.value);
setDepartment(event.target.value);
};
return (
<Autocomplete
id="Department"
value={department}
helperText={error.department}
options={Department_list}
getOptionLabel={option => typeof option === 'string' ? option : option.label}
onChange = {handleChangeDepartment}
renderInput={(params) => <TextField {...params} label="Search Department" variant="outlined" placeholder="Add Department" />}
/>
)
}
Ciao, in Autocomplete component event.target.value will be always 0. If you want to get the selected department you could use value in handleChangeDepartment. So your code becomes:
const handleChangeDepartment = (event, values) => {
console.log(event.target.value); // print always 0
console.log(values); // print values selected like { label: 'Department1', id: 1 }
setDepartment(values.label); // set department with values.label
};
Here a codesandbox example.
Rather than using:
event.target.value
try using:
event.target.innerText
or, to find the option index, use:
event.target.dataset.optionIndex
I'm working on a react native application.
I get the result of an SQL query like this:
const [ food, setFood ] = useState([]);
const load_food = async () => {
db.listProduct().then(row => setFood(row))
};
useFocusEffect( () => { load_food(food) }, [ food ] );
If I make a log like this:
console.log(food[i].PRODUCTNAME)
I get the name of my ingredient:
"Orange Juice"
Later I want to create a list from the variable food
const [listData, setListData] = useState(
Array(10)
.fill('')
.map((_, i) => ({ key: `${i}`, text: `Product name: ${food[i].PRODUCTNAME}`}))
);
But I have the following error:
undefined is not an object (evaluating 'food[i].PRODUCTNAME')
I imagine it's a synchronization issue. But I don't know how to solve it
You're mainly correct, it could be a sync problem, and you can use some techniques to avoid it, useEffect is one of them
const [listData, setListData] = useState([]);
useEffect(()=> {
setListData(
Array(10)
.fill('')
.map((_, i) => ({ key: `${i}`, text: `Product name: ${food[i].PRODUCTNAME}`}))
)
}, [food]);
This will only set listData state when food is updated, but also you will have to check food has at least 10 items or you will get undefined again
I have a config file with some fields for generating input elements inside a component.
I'm trying to generate an AsyncSelect input field and assigning it loadOptions prop through the config. Problem is that the function never gets called.
Here's the object in the configuration for generating the AsyncSelect input:
{
key: 'cityCode',
value: (customer, borrower) => ({
label: borrower.cityName,
value: borrower.cityCode
}),
text: 'city',
disabled: falseFn,
input: REACT_ASYNC_SELECT_INPUT,
props: (borrower, component) => ({
inputValue: component.state.cityName,
loadOptions: () => {
return CitiesService.find(component.state.cityName || '')
.then(records => records.map(record => ({
label: record.name,
value: record.code
})));
},
onInputChange: cityName => {
component.setState({
cityName
});
},
onChange: ({label, value}, {action}) => {
if (action === 'clear') {
component.updateCustomer(component.props.fieldKey + 'cityName', '');
component.updateCustomer(component.props.fieldKey + 'cityCode', -1);
} else {
component.updateCustomer(component.props.fieldKey + 'cityName', label);
component.updateCustomer(component.props.fieldKey + 'cityCode', value);
}
}
}),
render: trueFn
},
Here's the part of the component render utilizing the config file to render different inputs:
<div className="values">
{
BorrowerInfoConfig().map(field => {
if (field.render(borrower)) {
const kebabCaseKey = _.kebabCase(field.key);
const fieldElement = React.cloneElement(field.input, {
className: `${kebabCaseKey}-input`,
value: field.value ? field.value(customer, borrower) : _.get(borrower, field.key),
onChange: e => {
let value = e.target.value;
if (field.options) {
value = Number(value);
}
this.updateCustomer(fieldKey + field.key, value);
},
disabled: field.disabled(borrower),
...field.props(borrower, this)
}, field.options ? Object.keys(field.options).map(option => <option
key={option}
className={`${kebabCaseKey}-option`}
value={option}>
{field.options[option]}
</option>) : null);
return <div key={field.key} className={`value ${kebabCaseKey}`}>
<span>{field.text}</span>
{fieldElement}
</div>;
}
return null;
})
}
</div>
As you can see I use React.cloneElement to clone the input from the config file and assign new properties to it depends on what I get from the config file, in this case 4 custom props:
inputValue
loadOptions
onInputChange
onChange
My problem is that loadOptions is never called, any ideas why? On the other hand inputValue is assign correctly to the cityName state of the component.
My Problem was that REACT_ASYNC_SELECT_INPUT references normal Select and not Select.Async.
I want to change it by using contact-select, but I keep getting an error.
When I select this option, I can see this error. help me plz.
error
this is options
const options = [
{ value: 'descript', label: '주관식' },
{ value: 'choice', label: '객관식' },
{ value: 'combine', label: '다중식' }
];
and this is onChange fuction
onChangeTmpType = (e) => {
this.setState({
tmp_type: e.target.value
})
}
this is React-Select
<Select
components={makeAnimated()}
value={this.state.tmp_type}
onChange={this.onChangeTmpType}
options={options}
/>
As specified in the documentation here, the onChange function looks like this:
function (
One of <
Object,
Array<Object>,
null,
undefined
>,
{
action required One of <
"select-option",
"deselect-option",
"remove-value",
"pop-value",
"set-value",
"clear",
"create-option"
>
}
) => undefined
and the e const you declare actually has the following structure:
{
label: ...,
value: ...
}
so no target key here but directly e.value if what you want is to access the props value.
Here a live example with console.log so you can see what's happening.
Replace your onChangeTmpType function as follows.
onChangeTmpType = (e) => {
this.setState({
tmp_type: e.value
})
}
The reason for this as (#Laura mentioned before) is that the e from react select only contains the value and the label .