Find element has className in enzyme not found with renderoption autocompleted material - reactjs

I'm testing react component use enzyme and jest test. I'm try use find method in enzyme but it not found, i'm sure this element have been render because when I print actionClass const it return value "ts-cbb-item".
I have a combobox component:
/* eslint-disable no-use-before-define */
import Autocomplete from '#material-ui/lab/Autocomplete';
import PropTypes from 'prop-types';
import React, { useState, useRef } from 'react';
import './index.scss';
import InputCombobox from './input-combobox';
const ComboBox = (props) => {
const {
loading,
options,
onDataBinding,
onChange,
customRenderItem,
placeholder,
renderStartAdornment,
closeIcon,
disabled,
customGetOptionLabel,
onInputChange,
style,
clearOnBlur = false,
defaultValue,
...rest
} = props;
const currentSearch = useRef();
const [currentOption, setOption] = useState(null);
const [isInput, setIsInput] = useState(false);
const handleInputChange = (_, value, reason) => {
const isReasonInput = reason === 'input';
if (isReasonInput) {
setIsInput(false);
if (onInputChange)
onInputChange(value);
}
currentSearch.current = value;
if (value?.length < 3) return;
if (isReasonInput) onDataBinding(value);
}
const handleChangeOpt = (opt) => {
setIsInput(true);
if (onChange) onChange(opt);
}
return (
<Autocomplete
clearOnBlur={clearOnBlur}
disabled={disabled}
closeIcon={closeIcon}
className="ts-combobox"
options={options}
loading={loading}
onInputChange={handleInputChange}
defaultValue={defaultValue}
getOptionLabel={(option) => customGetOptionLabel
? customGetOptionLabel(option)
: option.label}
getOptionSelected={option => {
if (!currentOption || !currentOption.value) return false;
return option.value === currentOption.value;
}}
style={style ? style : { width: '100%' }}
renderOption={(option, state) => {
const actionClass = state?.selected ? "ts-ccb-item active" : "ts-ccb-item";
console.log('class:', actionClass);
return <div
onClick={() => {
setOption(option);
handleChangeOpt(option);
}}
className={actionClass}>
{ customRenderItem
? customRenderItem(option, currentSearch)
: option.label }
</div>
}}
);
}
export default ComboBox;
This is my test :
let initProps = {
loading: false,
options: [],
onDataBinding: () => {},
onChange: () => {},
customRenderItem: () => {},
renderStartAdornment: () => {},
closeIcon: null,
disabled: false,
customGetOptionLabel: () => {},
onInputChange: () => {},
style: null,
clearOnBlur: false,
placeholder: '',
defaultValue: null
}
const options = [
{
label: 'Cristiano Ronaldo',
value: 'Portugal'
},
{
label : 'Leo Messi',
value : 'Argentina'
},
{
label : 'Jesse Lingard',
value : 'England'
}
]
const event = {
preventDefault() {},
}
const onInputChangeMockFn = jest.fn((value) => value);
const onDataBindingMockFn = jest.fn( (value) => value? true: false);
const renderStartAdornmentMockFn = jest.fn((option) => option ? option.value : null );
const customGetOptionLabelMockFn = jest.fn((option) => option? option.label : null)
const renderInputParams = {
id: '',
disabled: false,
fullWidth: true,
size: 'small',
InputLabelProps: {},
InputProps: {},
inputProps: {}
}
it("Test_Comobox_With_RenderInput_Active(RenderStartAdornment_Have_Value)", () => {
initProps.renderStartAdornment = renderStartAdornmentMockFn;
initProps.customGetOptionLabel = customGetOptionLabelMockFn;
initProps.options = options;
const wrapper = mount(
<ComboBox {...initProps} />
);
const autoCompleted = wrapper.find(Autocomplete);
autoCompleted.props().renderOption(options[1], autoCompletedRenderOptionState);
autoCompleted.props().renderInput(renderInputParams);
expect(autoCompleted.find('div .ts-cbb-item')).toHaveLength(1);
const inputCombobox = wrapper.find(InputCombobox);
expect(inputCombobox.props().renderStartAdornment).toBeUndefined();
})
How can I find exactly element div has ClassName 'ts-cbb-item' in this case?

Related

Using DraftJS in a Functional Component

I am trying to implement DraftJS within an existing functional component and I am unable to figure out how to do this. It appears that all of the documentation and user-submitted content refers to class components.
I try to set it up using the following:
import { Editor } from "react-draft-wysiwyg";
import { EditorState } from 'draft-js'
export default function myFunctionalComponent() {
const [editorState, setEditorState] = useState(EditorState.createEmpty())
return(
<Editor
editorState={editorState}
onChange={setEditorState}
/>
)
}
However, unfortunately, I get this error in the console:
Warning: Can't call setState on a component that is not yet mounted.
This is a no-op, but it might indicate a bug in your application.
Instead, assign to this.state directly or define a state = {};
class property with the desired state in the r component.
Is there a way to make this work in a functional component?
As my very first answer in StackOverflow. :)
I took the example from https://github.com/facebook/draft-js/blob/main/examples/draft-0-10-0/rich/rich.html and converted it into a functional components 'RTEditor', .. .
Use the component with setContent as a prop. It takes the function to update parent elements state from useState
const [content, setContent] = useState<any>({})
...
<RTEditor setContent={setContent} />
RTEditor.tsx
import React, { useState, useRef } from 'react'
import {
Editor,
EditorState,
RichUtils,
getDefaultKeyBinding,
ContentBlock,
DraftHandleValue,
convertFromHTML,
convertFromRaw,
convertToRaw,
ContentState,
RawDraftContentState,
} from 'draft-js'
import 'draft-js/dist/Draft.css'
import BlockStyleControls from './BlockStyleControls'
import InlineStyleControls from './InlineStyleControls'
type Props = {
setContent: (state: RawDraftContentState) => void
}
const RTEditor = ({ setContent }: Props) => {
const editorRef = useRef(null)
const [editorState, setEditorState] = useState(EditorState.createEmpty())
const styleMap = {
CODE: {
backgroundColor: 'rgba(0, 0, 0, 0.05)',
fontFamily: '"Inconsolata", "Menlo", "Consolas", monospace',
fontSize: 16,
padding: 2,
},
}
const getBlockStyle = (block: ContentBlock) => {
switch (block.getType()) {
case 'blockquote':
return 'RichEditor-blockquote'
default:
return ''
}
}
const onChange = (state: EditorState) => {
setEditorState(state)
setContent(convertToRaw(editorState.getCurrentContent()))
}
const mapKeyToEditorCommand = (e: any): string | null => {
if (e.keyCode === 9 /* TAB */) {
const newEditorState = RichUtils.onTab(e, editorState, 4 /* maxDepth */)
if (newEditorState !== editorState) {
onChange(newEditorState)
}
return null
}
return getDefaultKeyBinding(e)
}
const handleKeyCommand = (
command: string,
editorState: EditorState,
eventTimeStamp: number
): DraftHandleValue => {
const newState = RichUtils.handleKeyCommand(editorState, command)
if (newState) {
onChange(newState)
return 'handled'
}
return 'not-handled'
}
const toggleBlockType = (blockType: string) => {
onChange(RichUtils.toggleBlockType(editorState, blockType))
}
const toggleInlineStyle = (inlineStyle: string) => {
onChange(RichUtils.toggleInlineStyle(editorState, inlineStyle))
}
return (
<>
<BlockStyleControls
editorState={editorState}
onToggle={toggleBlockType}
/>
<InlineStyleControls
editorState={editorState}
onToggle={toggleInlineStyle}
/>
<Editor
ref={editorRef}
editorState={editorState}
placeholder='Tell a story...'
customStyleMap={styleMap}
blockStyleFn={(block: ContentBlock) => getBlockStyle(block)}
keyBindingFn={(e) => mapKeyToEditorCommand(e)}
onChange={onChange}
spellCheck={true}
handleKeyCommand={handleKeyCommand}
/>
</>
)
}
export default React.memo(RTEditor)
BlockStyleControls.tsx
import React from 'react'
import { EditorState } from 'draft-js'
import StyleButton from './StyleButton'
const BLOCK_TYPES = [
{ label: 'H1', style: 'header-one' },
{ label: 'H2', style: 'header-two' },
{ label: 'H3', style: 'header-three' },
{ label: 'H4', style: 'header-four' },
{ label: 'H5', style: 'header-five' },
{ label: 'H6', style: 'header-six' },
{ label: 'Blockquote', style: 'blockquote' },
{ label: 'UL', style: 'unordered-list-item' },
{ label: 'OL', style: 'ordered-list-item' },
{ label: 'Code Block', style: 'code-block' },
]
type Props = {
editorState: EditorState
onToggle: (bockType: string) => void
}
const BlockStyleControls = ({ editorState, onToggle }: Props) => {
const selection = editorState.getSelection()
const blockType = editorState
.getCurrentContent()
.getBlockForKey(selection.getStartKey())
.getType()
return (
<div className='RichEditor-controls'>
{BLOCK_TYPES.map((type) => (
<StyleButton
key={type.label}
active={type.style === blockType}
label={type.label}
onToggle={onToggle}
style={type.style}
/>
))}
</div>
)
}
export default React.memo(BlockStyleControls)
InlineStyleControls.tsx
import React from 'react'
import { EditorState } from 'draft-js'
import StyleButton from './StyleButton'
const INLINE_STYLES = [
{ label: 'Bold', style: 'BOLD' },
{ label: 'Italic', style: 'ITALIC' },
{ label: 'Underline', style: 'UNDERLINE' },
{ label: 'Monospace', style: 'CODE' },
]
type Props = {
editorState: EditorState
onToggle: (bockType: string) => void
}
const InlineStyleControls = ({ editorState, onToggle }: Props) => {
const currentStyle = editorState.getCurrentInlineStyle()
return (
<div className='RichEditor-controls'>
{INLINE_STYLES.map((type) => (
<StyleButton
key={type.label}
active={currentStyle.has(type.style)}
label={type.label}
onToggle={onToggle}
style={type.style}
/>
))}
</div>
)
}
export default React.memo(InlineStyleControls)
StyleButton.tsx
import React from 'react'
type Props = {
active: boolean
style: string
label: string
onToggle: (bockType: string) => void
}
const StyleButton = ({ active, style, label, onToggle }: Props) => {
const _onToggle = (e: any) => {
e.preventDefault()
onToggle(style)
}
const className = 'RichEditor-styleButton'
return (
<button
className={className + `${active ? ' RichEditor-activeButton' : ''}`}
onClick={_onToggle}
>
{label}
</button>
)
}
export default React.memo(StyleButton)
Sry for not covering all typings. Hope that helps.
I was able to solve this using React useCallback hook and it works for me.
import { EditorState } from 'draft-js'
export default function myFunctionalComponent() {
const [editorState, setEditorState] = useState(EditorState.createEmpty())
const onEditorStateChange = useCallback(
(rawcontent) => {
setEditorState(rawcontent.blocks[0].text);
},
[editorState]
);
return(
<Editor
placeholder="Tell a story..."
onChange={onEditorStateChange}
/>
)
}

How can I implement not only one but multi setting toggles?

I use Shopify Polaris's setting toggle.https://polaris.shopify.com/components/actions/setting-toggle#navigation
And I want to implement not only one but multi setting toggles.But I don't want to always duplicate same handleToggle() and values(contentStatus, textStatus) like below the sandbox A,B,C...
import React, { useCallback, useState } from "react";
import { SettingToggle, TextStyle } from "#shopify/polaris";
export default function SettingToggleExample() {
const [activeA, setActiveA] = useState(false);
const [activeB, setActiveB] = useState(false);
const handleToggleA = useCallback(() => setActiveA((active) => !active), []);
const handleToggleB = useCallback(() => setActiveB((active) => !active), []);
const contentStatusA = activeA ? "Deactivate" : "Activate";
const contentStatusB = activeB ? "Deactivate" : "Activate";
const textStatusA = activeA ? "activated" : "deactivated";
const textStatusB = activeB ? "activated" : "deactivated";
const useHandleToggle = (active, setActive) => {
const handleToggle = useCallback(() => setActive((active) => !active), []);
const contentStatus = active ? "Disconnect" : "Connect";
const textStatus = active ? "connected" : "disconnected";
handleToggle();
return [contentStatus, textStatus];
};
useHandleToggle(activeA, setActiveA);
return (
<>
<SettingToggle
action={{
content: contentStatusA,
onAction: handleToggleA
}}
enabled={activeA}
>
This setting is <TextStyle variation="strong">{textStatusA}</TextStyle>.
</SettingToggle>
<SettingToggle
action={{
content: contentStatusB,
onAction: handleToggleB
}}
enabled={activeB}
>
This setting is <TextStyle variation="strong">{textStatusB}</TextStyle>.
</SettingToggle>
</>
);
}
https://codesandbox.io/s/vigorous-pine-k0dpib?file=/App.js
So I thought I can use a custom hook. But it's not working. So it would be helpful if you give me some advice.
Using simple Booleans for each toggle
If you combine your active state objects into a single array, then you can update as many settings as you would like dynamically. Here's an example of what that might look like:
import React, { useCallback, useState } from "react";
import { SettingToggle, TextStyle } from "#shopify/polaris";
export default function SettingToggleExample() {
// define stateful array of size equal to number of toggles
const [active, setActive] = useState(Array(2).fill(false));
const handleToggle = useCallback((i) => {
// toggle the boolean at index, i
setActive(prev => [...prev.slice(0,i), !prev[i], ...prev.slice(i+1)])
}, []);
return (
<>
{activeStatuses.map((isActive, index) =>
<SettingToggle
action={{
content: isActive ? "Deactivate" : "Activate",
onAction: () => handleToggle(index)
}}
enabled={isActive}
>
This setting is <TextStyle variation="strong">{isActive ? "activated" : "deactivated"}</TextStyle>.
</SettingToggle>
}
</>
);
}
Of course, you will likely want to add a label to each of these going forward, so it may be better to define a defaultState object outside the function scope and replace the Array(2).fill(false) with it. Then you can have a string label property for each toggle in addition to a boolean active property which can be added next to each toggle in the .map(...).
With labels added for each toggle
Per your follow up, here is the implementation also found in the CodeSandbox for a state with labels for each toggle (including here on the answer to protect against link decay):
import React, { useCallback, useState } from "react";
import { SettingToggle, TextStyle } from "#shopify/polaris";
const defaultState = [
{
isActive: false,
label: "A"
},
{
isActive: false,
label: "B"
},
{
isActive: false,
label: "C"
}
];
export default function SettingToggleExample() {
const [active, setActive] = useState(defaultState);
const handleToggle = useCallback((i) => {
// toggle the boolean at index, i
setActive((prev) => [
...prev.slice(0, i),
{ ...prev[i], isActive: !prev[i].isActive },
...prev.slice(i + 1)
]);
}, []);
return (
<div style={{ height: "100vh" }}>
{active?.map(({ isActive, label }, index) => (
<SettingToggle
action={{
content: isActive ? "Deactivate" : "Activate",
onAction: () => handleToggle(index)
}}
enabled={isActive}
key={index}
>
This {label} is 
<TextStyle variation="strong">
{isActive ? "activated" : "deactivated"}
</TextStyle>
.
</SettingToggle>
))}
</div>
);
}
My first attempt to refactor would use a parameter on the common handler
const handleToggle = useCallback((which) => {
which === 'A' ? setActiveA((activeA) => !activeA)
: setActiveB((activeB) => !activeB)
},[])
...
<SettingToggle
action={{
content: contentStatusA,
onAction: () => handleToggle('A')
}}
enabled={activeA}
>
It functions, but feels a bit naïve. For something more React-ish, a reducer might be the way to go.
With a reducer
This seems cleaner, and is definitely more extensible if you need more toggles.
function reducer(state, action) {
switch (action.type) {
case "toggleA":
const newValueA = !state.activeA;
return {
...state,
activeA: newValueA,
contentStatusA: newValueA ? "Deactivate" : "Activate",
textStatusA: newValueA ? "activated" : "deactivated"
};
case "toggleB":
const newValueB = !state.activeB;
return {
...state,
activeB: newValueB,
contentStatusB: newValueB ? "Deactivate" : "Activate",
textStatusB: newValueB ? "activated" : "deactivated"
};
default:
throw new Error();
}
}
const initialState = {
activeA: false,
activeB: false,
contentStatusA: "Activate",
contentStatusB: "Activate",
textStatusA: "deactivated",
textStatusB: "deactivated"
};
export default function SettingToggleExample() {
const [state, dispatch] = useReducer(reducer, initialState)
return (
<>
<SettingToggle
action={{
content: state.contentStatusA,
onAction: () => dispatch({type: 'toggleA'})
}}
enabled={state.activeA}
>
This setting is <TextStyle variation="strong">{state.textStatusA}</TextStyle>.
</SettingToggle>
<SettingToggle
action={{
content: state.contentStatusB,
onAction: () => dispatch({type: 'toggleA'})
}}
enabled={state.activeB}
>
This setting is <TextStyle variation="strong">{state.textStatusB}</TextStyle>.
</SettingToggle>
</>
);
}
With a wrapper component
A child component can eliminate the 'A' and 'B' suffixes
function reducer(state, action) {
switch (action.type) {
case "toggle":
const newValue = !state.active;
return {
...state,
active: newValue,
contentStatus: newValue ? "Deactivate" : "Activate",
textStatus: newValue ? "activated" : "deactivated"
};
default:
throw new Error();
}
}
const initialState = {
active: false,
contentStatus: "Activate",
textStatus: "deactivated",
};
const ToggleWrapper = () => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<SettingToggle
action={{
content: state.contentStatus,
onAction: () => dispatch({ type: "toggle" })
}}
enabled={state.active}
>
This setting is <TextStyle variation="strong">{state.textStatus}</TextStyle>.
</SettingToggle>
)
}
export default function SettingToggleExample() {
return (
<>
<ToggleWrapper />
<ToggleWrapper />
</>
);
}

How to implement react-autosuggest to load suggestions in "input-focus"?

I want to all the suggestions on "input-focus" and when the user further types the text so also the suggestions will appear in react-autosuggest.
import "./styles.css";
import Autosuggest from "react-autosuggest";
import { useState } from "react";
const options = ["Suggestion 1", "Suggestion 2", "Suggestion 3"];
export default function App() {
const [s, setS] = useState(options);
const [x, setX] = useState("Suggestion 1");
const [y, setY] = useState(false);
return (
<div className="App">
<Autosuggest
inputProps={{
value: x,
onChange: (event, { newValue }) => {
setX(newValue);
},
onFocus: () => {
setY(true);
},
onBlur: () => {
setY(false);
}
}}
suggestions={s}
onSuggestionsFetchRequested={({ value }) => {
setS(options.filter((x) => x.includes(value)));
}}
onSuggestionsClearRequested={() => {
setS([]);
}}
renderSuggestion={(text: string) => {
return <>{text}</>;
}}
getSuggestionValue={(a) => {
return a;
}}
alwaysRenderSuggestions={y}
/>
</div>
);
}
Code sandbox here. HTH.

How can I load my initialvalue in my form input?

I need to create a modify feature on a value. I have the input setup inside a modal but for some reason I can't seem to pass on the value to the initialValues.
My code starts with ActivityAreas
<Datatable
ref={this.datatable}
rows={this.props.activityAreas}
update={this.props.updateTable}
headers={[
{
key: "name",
title: () => t("entities.activityAreas.name"),
},
{
key: "action",
title: () => t("entities.activityAreas.action"),
render: (_, row) => (
<Flex>
<ClickableLink onClick={() => this.openModalDestroy(row)}>
{t("entities.activityAreas.destroy")}
</ClickableLink>
<ClickableLink onClick={() => this.openModalRename(row)}>
{t("entities.activityAreas.rename")}
</ClickableLink>
</Flex>
),
props: { align: "right" },
},
]}
type={ACTIVITY_AREAS}
/>
The line that says
this.openModalRename(row)
contains the actual line object with name and id. openModalRename looks like this right now
openModalRename = (row) => {
let modalProps = this.modalProps;
modalProps.row=row;
this.props.setModal(RenameActivityArea, modalProps, { width: '500px' })
}
It sends the data to the RenameActivityArea page
That page looks like this:
const RenameActivityArea = ({
values,
handleSubmit,
handleChange,
isValid,
hideModal,
isSubmitting,
setFieldValue,
...props
}) => {
const input = (name, inputProps) => (
<Input
{...getFormInputProps(
{
handleSubmit,
handleChange,
values,
isValid,
...props,
},
name,
inputProps
)}
/>
);
return (
<Form onSubmit={handleSubmit}>
<Header.H2>{t("settings.activityAreas.modActivityAreas")}</Header.H2>
{input("name", { label: "activityAreas.ActivityAreaName" })}
<ButtonRow
flex="0 0 auto"
flow="row"
justify="flex-end"
padding="10px 0px"
>
<Button type="button" outline onClick={hideModal}>
Fermer
</Button>
<Button
loading={isSubmitting}
disabled={!isValid}
onClick={handleSubmit}
>
{t("entities.activityAreas.save")}
</Button>
</ButtonRow>
</Form>
);
};
const initialValues = {
name: "",
};
const mapState = ({ entities }) => ({
activity_area: entities.activity_area,
});
const mapDispatch = (dispatch) => ({
onSubmit: (activity_area) => dispatch(updateActivityAreas(activity_area)),
});
RenameActivityArea.propTypes = {
modalProps: PropTypes.shape(),
handleSubmit: PropTypes.func,
handleChange: PropTypes.func,
hideModal: PropTypes.func,
isSubmitting: PropTypes.bool,
handleBlur: PropTypes.func,
errors: PropTypes.shape(),
touched: PropTypes.shape(),
isValid: PropTypes.bool,
};
const FormWrapped = withForm({
mapPropsToValues: () => initialValues,
validateOnChange: false,
validateOnBlur: true,
validationSchema: schema,
afterSubmit: (values, formik) =>
formik.props.afterModalSubmit(values, formik),
})(RenameActivityArea);
const Connected = connect(mapState, mapDispatch)(FormWrapped);
export default Connected;
I can get the value inside the box if I do this:
<Input {...getFormInputProps({
handleSubmit,
handleChange,
values: props.row, // <- This gives the value
isValid,
...props,
}, name, inputProps)}
/>
But then for some reason, I can't seem to be allowed to modify the value inside the input. It's like if it was read only or something. I am not sure thats the right way of approaching this anyway.
EDIT
getFormInputProps
export const getFormInputProps = (formProps, name, { label = name.replace('Attributes', ''), ...props } = {}) => {
const error = browseObject(formProps.errors, name)
const isTouched = browseObject(formProps.touched, name)
return {
onChange: formProps.handleChange,
label: label && t(`entities.${label}`),
error,
name,
value: browseObject(formProps.values, name) || '',
onBlur: formProps.handleBlur,
touched: isTouched,
...props,
}
}
withForm
export default withForm({
mapPropsToValues,
validateOnChange: false,
validateOnBlur: true,
validationSchema: schema,
afterSubmit: (_, { props: { history } }, { payload: { user } }) => {
history.push(getRedirect(user))
},
})
By adding the value inside the input field i can see it inside the modal but I can't type or change it. The Input does not have any read-only restrictions but still does not allow typing. So I might not be doing this the right way.
It looks like it uses Formik
https://formik.org/docs/overview
EDIT:
The utils/form is the following. As you can see it uses Formik
import { withFormik } from 'formik'
import { debounce } from 'lodash'
import objectToFormData from 'object-to-formdata'
import * as yup from 'yup'
import API from '../config/api'
import t from './translate'
import { DEFAULT_TEXT_WRAP, DEFAULT_TEXT_WRAP_THRESHOLD } from '../config/constants'
yup.setLocale({
mixed: {
required: () => t('errors.fieldIsRequired'),
},
})
const DEFAULT = (afterSubmit = () => { }) => ({
handleSubmit: (values, formik) => {
formik.props.onSubmit(values).then((action) => {
formik.setSubmitting(false)
afterSubmit(values, formik, action)
})
},
})
export default ({
afterSubmit = (values, { props: { afterSubmit: after } }) => after && after(values),
...attrs
}) => withFormik(Object.assign({}, DEFAULT(afterSubmit), attrs))
export async function checkExists(url, params) {
const response = await fetch(API.getUrl(url, params), {
method: 'GET',
headers: API.headers(),
})
return response
}
export const asyncSelectProps = formProps => key => ({
onChange: (items) => {
formProps.setFieldValue(key, items.map(item => item.id), false)
if (!formProps.touched[key]) {
formProps.setFieldTouched(key, true, false)
}
formProps.setFieldError(key, (!items.length) ? t('errors.mustContainAtLeastOneItem') : undefined, false)
},
touched: formProps.touched[key],
error: formProps.errors[key],
})
/* eslint prefer-arrow-callback: 0 */
/* eslint func-names: 0 */
yup.addMethod(yup.string, 'asyncUnique', function (url, body, message) {
return this.test({
name: 'asyncUnique',
message,
test: debounce(async (value) => {
if (value && value.length > 0) {
const response = await checkExists(`/exists/${url}`, body(value))
const { exists } = await response.json()
return !exists
}
return true
}, 500),
})
})
export const browseObject = (object, path) => {
const parsePath = (path.constructor === String) ? path.split('.') : path
const [key, ...rest] = parsePath
if (object && object[key] !== undefined) {
const next = object[key]
return (rest.length > 0) ? browseObject(next, rest) : next
}
return null
}
export const getFormInputProps = (formProps, name, { label = name.replace('Attributes', ''), ...props } = {}) => {
const error = browseObject(formProps.errors, name)
const isTouched = browseObject(formProps.touched, name)
return {
onChange: formProps.handleChange,
label: label && t(`entities.${label}`),
error,
name,
value: browseObject(formProps.values, name) || '',
onBlur: formProps.handleBlur,
touched: isTouched,
...props,
}
}
export const preventPropagate = callback => (event) => {
event.stopPropagation()
callback(event)
}
export const extractFiles = (files, multiple = false) => (multiple ? files : files[0])
export const fileInputHandler = (formProps, name, multiple = false) => (event) => {
const file = extractFiles(event.target.files, multiple)
formProps.setFieldValue(name, file)
}
export const wrapText = (
text,
maximum = DEFAULT_TEXT_WRAP,
threshold = DEFAULT_TEXT_WRAP_THRESHOLD,
) => {
if ((text.length + threshold) > maximum) {
return `${text.slice(0, maximum)}...`
}
return text
}
export const toFormData = body => objectToFormData(body)
export const PHONE_NUMBER_REGEX = /^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/
export const ZIP_CODE_REGEX = /^[A-Za-z]\d[A-Za-z][ -]?\d[A-Za-z]\d$/
EDIT:
So instead of using the const to render my input, I used directly an input field and no I can edit. But why did the const cause the problem?
const RenameActivityArea = ({
values,
touched,
handleSubmit,
handleChange,
handleBlur,
isValid,
hideModal,
isSubmitting,
setFieldValue,
...props
}) => {
return (
<Form onSubmit={handleSubmit}>
<Header.H2>{t('settings.activityAreas.modActivityAreas')}</Header.H2>
<input
type="text"
onChange={handleChange}
onBlur={handleBlur}
value={values.name}
name="name"
/>
<ButtonRow flex="0 0 auto" flow="row" justify="flex-end" padding="10px 0px">
<Button type="button" outline onClick={hideModal}>Fermer</Button>
<Button loading={isSubmitting} disabled={!isValid} onClick={handleSubmit}>{t('entities.activityAreas.save')}</Button>
</ButtonRow>
</Form>
)
}

React Checkbox not updating in functional component

Here is my functional component: (Link to reproduce)
import React, { useState } from "react";
import Aux from "../../../../../../hoc/Auxilary/Auxilary";
const DropDownContainer = (props) =>
{
const CheckedArray = Array(props.Data.length);
for(let i = 0; i< CheckedArray.length; i++)
CheckedArray[i] = false;
const [Selected, setSelected] = useState({
filterName: props.Name,
filterObjects: props.Data,
checked: CheckedArray.slice(),
multiSelectAllowed: false,
});
const propsStructure = props.Data.map((elem, index) => (
{
title: elem,
isSelected: false,
id: index,
key: "DropDown "+index,
checkboxStyle: {
width:"20%",
},
contentStyle: {
width: "80%",
}
}
));
const CheckBoxHandler = (index) =>
{
const newSelection = Selected;
if(!Selected.multiSelectAllowed)
{
newSelection.checked.forEach((_,ind) => (newSelection.checked[ind] = false));
}
newSelection.checked[index] = !newSelection.checked[index];
setSelected(newSelection);
console.log(Selected.checked[index]);
console.log(Selected.checked);
}
const PropDiv = propsStructure.map((elem) => {
return <div className = "CheckBoxRow" key ={elem.key}>
<input
style = {elem.checkboxStyle} type = "checkbox" checked = {Selected.checked[elem.id]}
onChange = {() => {CheckBoxHandler(elem.id)}}
/>
<div style = {elem.contentStyle}>{elem.title}</div>
</div>
});
return(
<Aux>
<div className = "Boxing">
{PropDiv}
</div>
</Aux>
);
}
export default DropDownContainer;
/*
from
props = ["a","b","c"]
to
propsStructure = [
{
title:,
isSelected:,
}
]
*/
As per my code... when I print Selected.checked the value gets updated correctly. But it doesn't reflect in checkbox of the UI. As you can see here:
Can anyone point out the way to solve this? Here is the link to reproduce. As you can see in the console, the values are updating correctly but in my checkbox, it takes the initial value which is passed at the beginning which is false in this case and so despite me trying to check it, it always remains unchecked.
i am change code .
import React, { useState } from "react";
const DropDownContainer = (props) => {
const CheckedArray = Array(props.Data.length);
for (let i = 0; i < CheckedArray.length; i++) CheckedArray[i] = false;
const [Selected, setSelected] = useState({
filterName: props.Name,
filterObjects: props.Data,
checked: CheckedArray.slice(),
multiSelectAllowed: false
});
const propsStructure = props.Data.map((elem, index) => ({
title: elem,
isSelected: false,
id: index,
key: "DropDown " + index,
checkboxStyle: {
width: "20%"
},
contentStyle: {
width: "80%"
}
}));
const CheckBoxHandler = (index) => {
let newSelection = { ...Selected };
newSelection.checked[index] = !newSelection.checked[index];
setSelected(newSelection);
console.log(Selected.checked[index]);
console.log(Selected.checked);
};
const PropDiv = propsStructure.map((elem) => {
return (
<div className="CheckBoxRow" key={elem.key}>
<input
style={elem.checkboxStyle}
type="checkbox"
checked={Selected.checked[elem.id]}
onClick={() => {
CheckBoxHandler(elem.id);
}}
/>
<div style={elem.contentStyle}>{elem.title}</div>
</div>
);
});
return (
<div>
<div className="Boxing">{PropDiv}</div>
</div>
);
};
export default DropDownContainer;
/*
from
props = ["a","b","c"]
to
propsStructure = [
{
title:,
isSelected:,
}
]
*/
**Working Demo:**

Resources