value cannot be set in expected object format - javascript

I am using react-select and redux form for autocomplete. There is a field called listings_of which is an array of data i need to post to the server. For this I used FieldArray concept. The data i get is not in value and label format which react-select expects so I have to use getOptionLabel and getOptionValue props. For this i did the following way
const renderListingsOf = ({
fields,
data,
meta: { touched, error, submitFailed }
}) => {
return (
<ul>
<li>
<button type="button" onClick={() => fields.push({})}>
Add Member
</button>
</li>
{fields.map((listing, index) => (
<li key={index}>
<button
type="button"
title="Remove Member"
onClick={() => fields.remove(index)}
/>
<Field
name={`${listing}`}
component={InputTextWithSearch}
placeholder="Search..."
options={(data && data.users.data) || []}
getOptionLabel={option =>
!isEmptyObject(option) && option.personal.first_name
}
getOptionValue={option => {
if(!isEmptyObject(option)) {
return {
_id : option._id,
first_name: option.personal.first_name,
last_name : option.personal.last_name
};
}
}}
/>
</li>
))}
</ul>
);
};
<Column>
<Query
query={GET_USERS}
variables={{
param: {
limit: 10
}
}}
>
{({ loading, data }) => {
if(loading)return 'loading...';
return (
<InputFieldWrapper styling={styling} label="Listings of">
<FieldArray
name="general.general_information.listings_of"
component={renderListingsOf}
data={data}
/>
</InputFieldWrapper>
);
}}
</Query>
</Column>
data.users.data has the following object
[
{
_id: 'sdfskfjsdkjfsd',
personal: {
first_name: 'hello',
last_name: 'hy'
}
},
{
_id: 'sdjfkjfdkjskfdjkfff',
personal: {
first_name: 'hello1',
last_name: 'hy1'
}
},
]
but when submitting the value for general.general_information.listings_of should be an array of object as below
general: {
general_information: {
listings_of: [
{
_id: 'sdfskfjsdkjfsd',
first_name: 'hello',
last_name: 'hy'
}
]
}
}
thus i tried to return the following object from getOptionValue but its not working its like this
general: {
general_information: {
listings_of: [
{
_id: 'sdfskfjsdkjfsd',
personal: {
first_name: 'hello',
last_name: 'hy'
}
}
]
}
}

Related

Checkbox values are not updating when state is set: ReactJS

I am having an app with two sections. Left section contains the categories and the right section containing the items under it. Under each category, I have the button to select all or unselect all items. I see the state changes happening in the code ( it is pretty printed inside HTML) but the checkbox values are not getting updated. Can someone help?
https://codesandbox.io/s/zealous-carson-dy46k8?file=/src/App.js
export const RightSection = ({ name, apps, json, setJson }) => {
function handleSelectAll(categoryName, type) {
const checked = type === "Select All" ? true : false;
const updated = Object.fromEntries(
Object.entries(json).map(([key, category]) => {
if (category.name !== categoryName) {
return [key, category];
}
const { name, tiles, ...rest } = category;
return [
key,
{
name,
...rest,
tiles: tiles.map((item) => ({
...item,
checked
}))
}
];
})
);
setJson(updated);
}
return (
<>
<div>
<input
type="button"
value={`select all under ${name}`}
onClick={() => handleSelectAll(name, "Select All")}
/>
<input
type="button"
value={`unselect all under ${name}`}
onClick={() => handleSelectAll(name, "Unselect All")}
/>
<h4 style={{ color: "blue" }}>{name} Items</h4>
{apps.map((app) => {
return (
<section key={app.tileName}>
<input checked={app.checked} type="checkbox" />
<span key={app.tileName}>{app.tileName}</span> <br />
</section>
);
})}
</div>
</>
);
};
import { useEffect, useState, useMemo } from "react";
import { SidebarItem } from "./SideBarItem";
import { RightSection } from "./RightSection";
import "./styles.css";
export default function App() {
const dummyJson = useMemo(() => {
return {
cat1: {
id: "cat1",
name: "Category 1",
tiles: [
{
tileName: "abc",
searchable: true,
checked: false
},
{
tileName: "def",
searchable: true,
checked: true
}
]
},
cat2: {
id: "cat2",
name: "Category 2",
tiles: [
{
tileName: "ab",
searchable: true,
checked: true
},
{
tileName: "xyz",
searchable: true,
checked: false
}
]
},
cat3: {
id: "cat3",
name: "Category 3",
tiles: [
{
tileName: "lmn",
searchable: true,
checked: true
},
{
tileName: "",
searchable: false,
checked: false
}
]
}
};
}, []);
const [json, setJson] = useState(dummyJson);
const [active, setActive] = useState(dummyJson["cat1"]);
return (
<>
<div className="container">
<div>
<ul>
{Object.values(json).map((details) => {
const { id, name } = details;
return (
<SidebarItem
key={name}
name={name}
{...{
isActive: id === active.id,
setActive: () => setActive(details)
}}
/>
);
})}
</ul>
</div>
<RightSection
name={active.name}
apps={active.tiles}
{...{ json, setJson }}
/>
</div>
<p>{JSON.stringify(json, null, 2)}</p>
</>
);
}
since you have not updated data of checkbox (in your code) / logic is wrong (in codesandbox) do the following add this function in RightSection
...
function setTick(app, value: boolean) {
app.checked = value;
setJson({...json})
}
...
and onChange in input checkbox
<input
onChange={({ target }) => setTick(app, target.checked)}
checked={app.checked}
type="checkbox"
/>
Codesandbox: see line 25 -> 28 and line 48 in RightSection.tsx are the lines I added
For the two buttons select all and unselect all to update the state of the checkboxes, the data must be synchronized (here you declare active as json independent of each other, this makes the update logic complicated. Unnecessarily complicated, please fix it to sync
const [json, setJson] = useState(dummyJson);
const [activeId, setActiveId] = useState('cat1');
const active = useMemo(() => json[activeId], [json, activeId]);
and update depends:
<SidebarItem
key={name}
name={name}
{...{
isActive: id === activeId,
setActive: () => setActiveId(id)
}}
/>
Codesandbox: line 60 -> 63 and line 81 -> 82 in file App.js
https://codesandbox.io/s/musing-rhodes-yp40fi
the handleOperation function could also be rewritten very succinctly but that is beyond the scope of the question

mapping and filtering an array of objects with key

I have an array of objects for users:
const users = [
0: {
name: "John",
email: "jsmith#gmail.com"
},
1: {
name: "Bob",
email: "bsmith#gmail.com"
}
]
A useState that controls which user id is selected.
const [id, setId] = useState("1");
For example I have a default state set to id=1
I am trying to map and filter through the array of objects above to get the name, and email based on the id inside the array for the object.
Here is the code of what I am trying to do, but it doesn't seem to get the data from array of objects.
{Object.keys(users).filter((user) => user.id === id).map((user, index) => {
const userDetails = users[user]
return (
<div className="profile" key={index}>
<h1>{userDetails.name}</h1>
<h1>{userDetails.email}</h1>
</div>
);
})}
Any help would be appreciated.
users is an array of objects. So just use index without filter or map:
const userDetails = users[id]
return (
<div className="profile">
<h1>{userDetails.name}</h1>
<h1>{userDetails.email}</h1>
</div>
);
To add to the accepted answer:
Even if users was an object, Object.keys(users).filter((user) => user.id === id).map() is redundant if you're only interested in a single user. You can just access the users object directly by the selected id as a key.
import { useState } from "react";
const users = {
0: {
name: "John",
email: "jsmith#gmail.com"
},
1: {
name: "Bob",
email: "bsmith#gmail.com"
},
2: {
name: "Jill",
email: "jjones#gmail.com"
},
3: {
name: "Joan",
email: "jjohnson#gmail.com"
}
};
export default function App() {
const [selectedUserId, setSelectedUserId] = useState("0");
return (
<>
<div className="profile">
<h1>{users[selectedUserId].name}</h1>
<h1>{users[selectedUserId].email}</h1>
</div>
<label>
selected user id:
<input
type="number"
min={0}
max={Object.keys(users).length - 1}
value={selectedUserId}
onChange={(e) => {
setSelectedUserId(e.target.value);
}}
/>
</label>
</>
);
}
The syntax of your users declaration is wrong. Either change it to an array or an object. Now its a mix of both in wrong syntax.
The following code is a workable sample.
export default function App() {
const [id, setId] = useState("1");
const users = {
0: {
name: "John",
email: "jsmith#gmail.com"
},
1: {
name: "Bob",
email: "bsmith#gmail.com"
}
}
return (
<div>
<h1>Hello StackBlitz!</h1>
<p>Start editing to see some magic happen :)</p>
{!!users[id] && <div className="profile">
<h1>{ users[id].name}</h1>
<h1>{users[id].email}</h1>
</div>
}
</div>
);
}

How to validate the two field in one object using react hook with yup

{
label: 'Room',
name: 'room',
rule: yup.array(yup.object()).required(),
renderer: (data: any) => {
const { control, register, errors } = useFormContext();
return (
<div className="block w-full">
{
teacherRole && teacherRole.map((item, idx) => (
<div key={idx} className="flex pb-2 items-center">
<SelectPicker
placeholder={'TEACHER'}
data={
teacherList && teacherList.map(x => (
{ label: x.name, value: x.id }
))
}
onChange={(val) => control.setValue('room', setTeacher(val, idx))}
value={control.getValues()['selectTeacher']}
style={{ width: '100%' }}
/>
<span className="px-2 leading-8 text-2xl">-</span>
<SelectPicker
data={[
{ label: 'Admin', value: 'ROLE_ADMIN' },
{ label: 'Teacher', value: 'ROLE_TEACHER' },
{ label: 'Student', value: 'ROLE_STUDENT' },
]}
placeholder={'Role'}
onChange={(val) => control.setValue('room', setRole(val, idx))}
value={control.getValues()['selectRole']}
style={{ width: '100%', paddingRight: '.3rem' }}
/>
</div>
))
}
</div>
)
}
}
What I'm trying to do here is to validate the teacher and the room when its empty.
cause when I try to submit the form, even its null it will submit.
What I want is to add a validation when the role or teacher is null the error message should be appear.
I don't know the type of teacher and role so I assumed it. Hope this ex help.
Yup.array().of(
Yup.object().shape({
teacher: Yup.string().required("teacher required"),
role: Yup.string()
.required("role required")
})
)
})

React select change the selected value

i am tying to build a multi select of items. my backend data structure is an array of objects like this
{
"selectedItems": [
{"_id" : ""}
]
}
the problem with react-select is when i select one or many items, the structure does not match with my backend route, it displays like this
{
"selectedItems": [
{"value" : "", label : ""}
]
}
i am working with Formik to manage the form and you can also see the result on this sandbox on console log
const ItemSelected = () => {
const items = [
{
_id : "123", name : "john", desc : 'eb'
},
{
_id : "456", name : "doe", desc : 'ec'
},
{
_id : "789", name : "seal", desc : 'ef'
}
]
const itemList = (options) => {
return (
options &&
options.map(option => {
return {
value: option._id,
label: option.name
};
})
);
}
return(
<div>
<Formik
initialValues={{
selectedItems : []
}}
onSubmit={values => {
console.log(values)
}}
>
{({
values,
handleSubmit,
setFieldValue
}) => (
<Form onSubmit={handleSubmit}>
<div className="row">
<div className="col">
<Select
isMulti
name={`selectedItems`}
value={values.selectedItems}
onChange={e=>setFieldValue(`selectedItems`, e)}
options={itemList(items)}
className="basic-multi-select"
classNamePrefix="select"
/>
</div>
<div className="col">
<button type="submit">
submit
</button>
</div>
</div>
</Form>
)}
</Formik>
</div>
)
}
You need to use map to create the data structure you want when you handle submit.
onSubmit={values => {
if(values.selectedItems){
const data = values.selectedItems.map(value => ({_id: value.value}))
console.log(data);
}
}}

Using JsonSchemaForm on change to update field's content

I am trying to use JsonSchema-Form component but i ran into a problem while trying to create a form that, after choosing one of the options in the first dropdown a secondary dropdown should appear and give him the user a different set o options to choose depending on what he chose in the first dropdown trough an API call.
The thing is, after reading the documentation and some examples found here and here respectively i still don't know exactly how reference whatever i chose in the first option to affect the second dropdown. Here is an example of what i have right now:
Jsons information that are supposed to be shown in the first and second dropdowns trough api calls:
Groups: [
{id: 1,
name: Group1}
{id: 2,
name: Group2}
]
User: [User1.1,User1.2,User2.1,User2.2,User3.1,User3.2, ....]
If the user selects group one then i must use the following api call to get the user types, which gets me the the USER json.
Component That calls JSonChemaForm
render(){
return(
<JsonSchemaForm
schema={someSchema(GroupOptions)}
formData={this.state.formData}
onChange={{}}
uiSchema={someUiSchema()}
onError={() => {}}
showErrorList={false}
noHtml5Validate
liveValidate
>
)
}
SchemaFile content:
export const someSchema = GroupOptions => ({
type: 'object',
required: [
'groups', 'users',
],
properties: {
groups: {
title: 'Group',
enum: GroupOptions.map(i=> i.id),
enumNames: GroupOptions.map(n => n.name),
},
users: {
title: 'Type',
enum: [],
enumNames: [],
},
},
});
export const someUISchema = () => ({
groups: {
'ui:autofocus': true,
'ui:options': {
size: {
lg: 15,
},
},
},
types: {
'ui:options': {
size: {
lg: 15,
},
},
},
});
I am not really sure how to proceed with this and hwo to use the Onchange method to do what i want.
I find a solution for your problem.There is a similar demo that can solve it in react-jsonschema-form-layout.
1. define the LayoutField,this is part of the demo in react-jsonschema-form-layout.To make it easier for you,I post the code here.
Create the layoutField.js.:
import React from 'react'
import ObjectField from 'react-jsonschema-form/lib/components/fields/ObjectField'
import { retrieveSchema } from 'react-jsonschema-form/lib/utils'
import { Col } from 'react-bootstrap'
export default class GridField extends ObjectField {
state = { firstName: 'hasldf' }
render() {
const {
uiSchema,
errorSchema,
idSchema,
required,
disabled,
readonly,
onBlur,
formData
} = this.props
const { definitions, fields, formContext } = this.props.registry
const { SchemaField, TitleField, DescriptionField } = fields
const schema = retrieveSchema(this.props.schema, definitions)
const title = (schema.title === undefined) ? '' : schema.title
const layout = uiSchema['ui:layout']
return (
<fieldset>
{title ? <TitleField
id={`${idSchema.$id}__title`}
title={title}
required={required}
formContext={formContext}/> : null}
{schema.description ?
<DescriptionField
id={`${idSchema.$id}__description`}
description={schema.description}
formContext={formContext}/> : null}
{
layout.map((row, index) => {
return (
<div className="row" key={index}>
{
Object.keys(row).map((name, index) => {
const { doShow, ...rowProps } = row[name]
let style = {}
if (doShow && !doShow({ formData })) {
style = { display: 'none' }
}
if (schema.properties[name]) {
return (
<Col {...rowProps} key={index} style={style}>
<SchemaField
name={name}
required={this.isRequired(name)}
schema={schema.properties[name]}
uiSchema={uiSchema[name]}
errorSchema={errorSchema[name]}
idSchema={idSchema[name]}
formData={formData[name]}
onChange={this.onPropertyChange(name)}
onBlur={onBlur}
registry={this.props.registry}
disabled={disabled}
readonly={readonly}/>
</Col>
)
} else {
const { render, ...rowProps } = row[name]
let UIComponent = () => null
if (render) {
UIComponent = render
}
return (
<Col {...rowProps} key={index} style={style}>
<UIComponent
name={name}
formData={formData}
errorSchema={errorSchema}
uiSchema={uiSchema}
schema={schema}
registry={this.props.registry}
/>
</Col>
)
}
})
}
</div>
)
})
}</fieldset>
)
}
}
in the file, you can define doShow property to define whether to show another component.
Next.Define the isFilled function in JsonChemaForm
const isFilled = (fieldName) => ({ formData }) => (formData[fieldName] && formData[fieldName].length) ? true : false
Third,after you choose the first dropdown ,the second dropdown will show up
import LayoutField from './layoutField.js'
const fields={
layout: LayoutField
}
const uiSchema={
"ui:field": 'layout',
'ui:layout': [
{
groups: {
'ui:autofocus': true,
'ui:options': {
size: {
lg: 15,
},
},
}
},
{
users: {
'ui:options': {
size: {
lg: 15,
},
},
doShow: isFilled('groups')
}
}
]
}
...
render() {
return (
<div>
<Form
schema={schema}
uiSchema={uiSchema}
fields={fields}
/>
</div>
)
}

Categories