Cannot read property 'map' of undefined - Error from ReactJS - javascript

Error Message:
Error: Cannot read property 'map' of undefined
React component:
const checkBox = props => {
return (
<div>
<label htmlFor={props.name} className='form-label'>
{props.title}
</label>
<div className='checkbox-group'>
{
props.options.map(option => {
return (
<label key={option}>
<input
className='form-checkbox'
id={props.name}
name={props.name}
onChange={props.handleChange}
value={option}
checked={props.selectedOptions.indexOf(option) > -1}
type='checkbox'
/>{' '}
{option}
</label>
)
})
}
</div>
</div>
)
}
In my code is anything going wrong? If yes please let me know. Can someone help me out from this?
The error
Cannot read property 'map' of undefined
Is thrown when the map function in the comment list component is executed.

A question comment references a tutorial showing how to build a form and its sub-components. A couple of the components include examples of how to pass props, but that's missing from <Checkbox/>.
To fill that gap, here's an example of how you might expect to use <Checkbox/>. I didn't read the article in its entirety, so I'm hoping you can help correct any mistakes I've made here and it gets you started on your own development.
<Checkbox
title={'Skills'}
name={'skills'}
options = {this.state.skills} <!-- array of skills or empty array -->
selectedOptions = {this.state.newUser.skills} <!-- array of skills or empty array -->
handleChange = {this.handleInput}
/>

You could also use a simple fallback like.
const checkBox = props => {
const { options, name, titlem, selectedOptions, handleChange } = props
return (
<div>
<label for={name} className="form-label">
{title}
</label>
<div className="checkbox-group">
{(options || []).map(option => {
return (
<label key={option}>
<input
className="form-checkbox"
id={name}
name={name}
onChange={handleChange}
value={option}
checked={selectedOptions.indexOf(option) > -1}
type="checkbox"
/>{" "}
{option}
</label>
);
})}
</div>
</div>
);
};

const checkBox = ({ options, name, title, selectedOptions, handleChange }) => {
return (
<div>
<label htmlFor={name} className='form-label'>
{title}
</label>
<div className='checkbox-group'>
{
options && options.map(option => {
return (
<label key={option}>
<input
className='form-checkbox'
id={name}
name={name}
onChange={handleChange}
value={option}
checked={selectedOptions.indexOf(option) > -1}
type='checkbox'
/>{' '}
{option}
</label>
)
})
}
</div>
</div>
)
}

Related

Conditional rendering inputs overlap text

I've been trying to make a form where, depending on a switch button, renders one or two different inputs.
My code looks like this:
const Test = () => {
const [switchButton, setSwitch] = React.useState(true)
const handleSwitch = () => {setstate(!switchButton)}
return <>
<Switch checked={switchButton} onClick={() => handleSwitch} />
{!switchButton ?
<>
<label htmlFor="input_1">Input 1</label>
<input id="input_1"/>
</>
:
<>
<label htmlFor="input_2">Input 2</label>
<input id="input_2" />
<label htmlFor="input_3">Input 3</label>
<input id="input_3" />
</>
}
</>
}
export default Test;
My issue happens when I enter characters into one input and then switch them: The same characters are shown on my new rendered input. Example here.
Why does this happen? I'm really lost
use keys for the elements, so it will not be recognized as the same element as react tries to map elements on each rerender if they were already rendered before.
so react sees: 1 input and than 2 inputs and thinks the first input is the same as before.
const Test = () => {
const [switchButton, setSwitch] = React.useState(true)
const handleSwitch = () => setSwitch((switch) => !switch)
return (
<>
<Switch checked={switch} onClick={handleSwitch} />
{!switchButton ? (
<React.Fragment key="key1">
<label htmlFor="input_1">Input 1</label>
<input id="input_1"/>
</>
) : (
<React.Fragment key="key2">
<label htmlFor="input_2">Input 2</label>
<input id="input_2" />
<label htmlFor="input_3">Input 3</label>
<input id="input_3" />
</>
)}
</>
)}
export default Test;
but anyway it would be better to use a controlled input. something like this:
const [value, setValue] = React.useState(true)
<input value={value} onChange={(e) => setValue(e.target.value)}/>

Make Whole Div Clickable in React

I have checkbox and its working well. However I wanted the whole div to be clickable also not just the checkbox alone.
Pls check here CLICK HERE
<form onSubmit={handleSubmit}>
{products.map((product) => (
<div key={product} style={{ cursor: "pointer" }}>
{product}
<Checkbox
name="products"
value={product}
checked={values.products.includes(product)}
onChange={({ target }) => {
let updatedProducts;
if (values.products.includes(product)) {
updatedProducts = values.products.filter(
(product) => product !== target.value
);
} else {
updatedProducts = [...values.products, target.value];
}
setFieldValue("products", updatedProducts);
}}
/>
</div>
))}
<button type="submit">Submit</button>
</form>
Wrap the checkbox in label element. This will make the text, i.e. "label" clickable as part of the input. Since label elements are display: inline you will need to also override the display CSS to maintain it as a block level element.
<form onSubmit={handleSubmit}>
{products.map((product) => (
<label key={product} style={{ cursor: "pointer", display: "block" }}>
{product}
<Checkbox
name="products"
value={product}
checked={values.products.includes(product)}
onChange={({ target }) => {
let updatedProducts;
if (values.products.includes(product)) {
updatedProducts = values.products.filter(
(product) => product !== target.value
);
} else {
updatedProducts = [...values.products, target.value];
}
setFieldValue("products", updatedProducts);
}}
/>
</label>
))}
<button type="submit">Submit</button>
</form>
If you want to have your product name clickable and change checkbox state, use a label with an htmlFor
You would need to make a few changes to your app like so:
<label for={product}>{product}</label> <!-- ADD LABEL TAG AND FOR HERE! <!--
<Checkbox
name="products"
id={product} // <-- HAVE YOUR ID SAME WITH THE FOR ABOVE
value={product}
checked={values.products.includes(product)}
onChange={({ target }) => {
let updatedProducts;
if (values.products.includes(product)) {
updatedProducts = values.products.filter(
(product) => product !== target.value
);
} else {
updatedProducts = [...values.products, target.value];
}
setFieldValue("products", updatedProducts);
}}
/>;
And inside Input component:
<Input
type="checkbox"
id={id} // <-- CHANGE ID TO USE ACTUAL ID ABOVE
name={name}
value={value}
checked={checked}
onChange={onChange}
/>;

Dependency Formik form Unit testing

I want to write the unit test for React formik form fields.
Related packages versions are bellow
React version ^16.9.0
Formik version ^1.5.8
Jest version ^23.0.0
jest-enzyme version ^7.1.2
Enzyme version ^3.11.0
Component.js
const formInitialValues = {
frequency: null,
weekType: null'
};
<Formik
enableReinitialize
initialValues={formInitialValues}
onSubmit={(values, actions) => {
console.log(values);
}}
validate={values => {
// validation here
}}
render={props => (
<Form
onSubmit={props.handleSubmit}
data-test="smart-cabinet-add-doc-form"
>
<Row>
<div className="form-group col-md-6 ">
<label htmlFor="frequency" data-test="frequency-label">
Frequency
</label>
<CustomSelect
id="frequency"
name="frequency"
className="form-control col-sm-10"
options={SmartCabinetHelper.ADD_DOCUMENT_FREQUENCY_OPTIONS}
onChange={(name, value) => {
setFieldValue(name, value);
formatDueDate(values);
}}
onBlur={setFieldTouched}
placeholder="Select Frequency"
setFieldValue={setFieldValue}
value={values.frequency}
isDisabled={isNull(values.uploadedBy)}
data-test="frequency-field"
/>
<div className="error-message">
<ErrorMessage name="frequency" />
</div>
</div>
</Row>
{!isNull(values.frequency) && (
<Row>
<div className="form-group col-md-12 ">
<CustomSelect
id="weekType"
name="weekType"
className="form-control"
options={QuickLinkModalHelper.WEEKS_TYPES.slice(0, -1)}
onChange={(name, value) => {
setFieldValue(name, value);
formatDueDate(values);
}}
onBlur={setFieldTouched}
isSearchable={false}
placeholder=""
setFieldValue={setFieldValue}
value={values.weekType}
data-test="weekType-field"
/>
</div>
</Row>
)}
</form>
)}
/>
In the componentTest.js file I can test the frequency element, but I can't test the weekType element because that element depends on the frequency value.
{!isNull(values.frequency) && (
When the unit test runs, according to the formInitialValues, of formik form frequency value is Null. So I can't check weekType, It occurs an error. Test fails.
ComponentTest.js
const setup = () => {
return mount(
<Component />
);
};
describe('Test form elements', () => {
let wrapper;
beforeEach(() => {
wrapper = setup(defaultProps);
});
test('test frequency field', () => {
const frequencyField = findByTestAttr(
wrapper,
'frequency-field'
);
expect(frequencyField.length).toBe(1); // This test passes
});
test('test weekType field', () => {
const weekTypeField = findByTestAttr(
wrapper,
'weekType-field'
);
expect(weekTypeField.length).toBe(1); // This test fails, because frequency value = null
});
});
I tried too many ways to figure that. But couldn't. Is there any way to change formInitialValues in the testing file? Thank you.
I tried many ways to find a solution, finally found a way
Use mock formik form in test file except importing component.js using react-testing library
component.test.js
describe('Test form elements', () => {
it('test frequency field', async () => {
const { getByText, getByTestId } = render(
<Formik
initialValues={{
frequency: null,
weekType: null' }}
onSubmit={mock}
>
<Form
onSubmit={props.handleSubmit}
data-test="smart-cabinet-add-doc-form"
>
<Row>
<div className="form-group col-md-6 ">
<label htmlFor="frequency" data-test="frequency-label">
Frequency
</label>
<CustomSelect
id="frequency"
name="frequency"
...
/>
<div className="error-message">
<ErrorMessage name="frequency" />
</div>
</div>
</Row>
{!isNull(values.frequency) && (
<Row>
<div className="form-group col-md-12 ">
<CustomSelect
id="weekType"
name="weekType"
...
data-test="weekType-field"
/>
</div>
</Row>
)}
</form>
</Formik>
);
const weekTypeField = await waitForElement(() => getByTestId('weekType-field'));
expect(weekTypeField.length).toBe(1);
});
});
Any suggestions...
Thank you.

React useState and map()

I'm trying to make an comment input from map,
but since I use the same useState all the input fields get changed.
How can I target a specific input?
return (
<div>
{posts.map(post => (
<div key={post.id}>
<img
src={`https://localhost:1111/api/posts/uploads/images/${post.content}`}
alt={`${post.id}`}
/>
<p>{post.description}</p>
<span>{post.likes ? post.likes : 0}</span>
<button onClick={() => like(post.id)}>Like</button>
<Link to={`/post/${post.id}`}>Edit</Link>
<button onClick={() => deletePost(post.id)}>Delete</button>
<form onSubmit={uploadComment}>
<input
type="text"
onChange={handleComment}
value={comment}
placeholder="Comment"
/>
</form>
</div>
))}
</div>
)
You have an own state per rendered post, which means that it is a use case for an own component:
function Post(post, deletePost) {
const [comment, setComment] = useState('');
const uploadComment = () => {}; // your code is missing
return (
<div key={post.id}>
<img
src={`https://localhost:1111/api/posts/uploads/images/${post.content}`}
alt={`${post.id}`}
/>
<p>{post.description}</p>
<span>{post.likes ? post.likes : 0}</span>
<button onClick={() => like(post.id)}>Like</button>
<Link to={`/post/${post.id}`}>Edit</Link>
<button onClick={() => deletePost(post.id)}>Delete</button>
<form onSubmit={uploadComment}>
<input
type="text"
onChange={e => setComment(e.target.value)}
value={comment}
placeholder="Comment"
/>
</form>
</div>
)
}
Then your render function would look like this:
return (
<div>
{posts.map(post => <Post post={post} deletePost={deletePost} />)}
</div>
)
Consider using react useState hook per input.

Reactjs Instant search

I have a JSON schema which I'm mapping through the page and then rendering. Now I want to filter through an only search query and render only the search results.
Listing.jsx
<div id="searchbar">
<Hero>
<Section>
<Container>
<Heading style={{ color: "white" }}>SEARCH</Heading>
<Field>
<Control>
<Input
onChange={this.onChangeSearch}
name="query"
type="text"
placeholder="Enter query"
value={query}
/>
</Control>
</Field>
</Container>
</Section>
</Hero>
</div>
<div>
{pageItems.map(data => (
<div key={data.id}>
{data.status === true ? (....) : (...)}</div>}
How can I show only relevant search results in instant search/real-time search?
As per comments/suggestions, I'd like the search based on a specific array or entire JSON object to search.
This is my onChange event which returns the whole JSON object in the console.
onChangeSearch = evt => {
this.setState({
[evt.target.name]: evt.target.value
});
this.state.items.map(data => {
return console.log(data);
});
};

Categories