Home > database >  can't use recursive map in react component
can't use recursive map in react component

Time:11-13

i stumbled into an issue i cant solve, i have an object 'customerDraft' which has nested object in it. i want to render every field plus the fields which are inside of 'customerDraft.metadata'.

my component looks like this:

const CustomerDetailEditModal = (props) => {
  const {
    open,
    setOpen,
    customerDraft,
    customerProfileDraft,
    setDraftCustomer,
    setDraftProfile,
    onUpdate
  } = props;

  const classes = useStyles();
  const dispatch = useDispatch();

  const [isPasswordHidden, setIsPasswordHidden] = useState(true);
  // const [attributes, setAttributes] = useState({});

  const projectId = useSelector(({ project }) => project.currentProject._id);
  const generatedPassword = useSelector(({ customer }) => customer.password);

  const isCurrentProjectCapstone = projectId === '4387564328756435';

  const onModalCancel = () => {
    setOpen(false);
    if (isCurrentProjectCapstone) {
      dispatch(removeItemFromCustomerDraftAction('password'));
    }
  };

  const generatePassword = () => {
    dispatch(getGeneratedPassword());
  };

  useEffect(() => {
    if (!generatedPassword) return;
    setDraftCustomer({
      ...customerDraft,
      password: generatedPassword
    });

    // eslint-disable-next-line
  }, [generatedPassword]);

  console.log(customerDraft);

  return (
    <div>
      <Modal
        bodyStyle={{
          fontSize: '12px',
          height: 500,
          margin: '0 auto'
        }}
        centered
        footer={
          <div
            style={{
              display: 'flex',
              justifyContent: 'flex-end'
            }}>
            <CButton
              htmlType="submit"
              onClick={(e) => {
                setOpen(false);
                e.preventDefault();
              }}
              size="large"
              type="secondary">
              Cancel
            </CButton>
            <CButton
              htmlType="submit"
              onClick={onUpdate}
              size="large"
              type="primary"
              // disabled={!isSaveEnabled}
            >
              Save
            </CButton>
          </div>
        }
        onCancel={onModalCancel}
        title={
          <span
            style={{
              fontSize: '24px',
              fontWeight: 700,
              lineHeight: '24px'
            }}>
            Edit User
          </span>
        }
        visible={open}
        width={customerProfileDraft ? 770 : 385}>
        <form className={classes.form} id="customer-edit-form">
          <div className={classes.wrapperDiv}>
            {Object.entries(customerDraft).map((item, i) => {
              if (customerDraft.fullName) {
                if (restrictedData.includes(item[0] || item[0].toLowerCase().includes('id'))) {
                  return false;
                }
              }
              if (restrictedData.includes(item[0]) || item[0].toLowerCase().includes('id')) {
                return false;
              }
              return (
                <CStandardInput
                  key={i}
                  allowClear
                  defaultValue={item[1]}
                  disableUnderline
                  formclasses={{ root: classes.root }}
                  htmlFor={`standard-customer-edit-${item[0]}`}
                  id={`standard-customer-edit-${item[0]}`}
                  label={item[0]}
                  onChange={(event) => {
                    setDraftCustomer({
                      ...customerDraft,
                      fullName: event.target.value
                    });
                    setDraftProfile({
                      ...customerProfileDraft,
                      fullName: event.target.value
                    });
                  }}
                  size="large"
                />
              );
            })}
            {isCurrentProjectCapstone && (
              <div className={classes.passwordWrapper}>
                <CStandardInput
                  adornment={
                    <>
                      <button
                        className={classes.buttonSvg}
                        onClick={() => {
                          navigator.clipboard.writeText(customerDraft.password || '');
                        }}
                        style={{
                          marginRight: '5px'
                        }}
                        type="button">
                        <img alt="copy password" src={copyIcon} />
                      </button>
                      <button
                        className={classes.buttonSvg}
                        onClick={() => setIsPasswordHidden(!isPasswordHidden)}
                        type="button">
                        <img
                          alt="toggle password visibility"
                          src={isPasswordHidden ? crossedEyeIcon : eyeIcon}
                        />
                      </button>
                    </>
                  }
                  disableUnderline
                  formclasses={{ root: classes.root }}
                  htmlFor="standard-input-user-password"
                  id="standard-input-user-password"
                  label="Password"
                  onChange={(e) => setDraftCustomer({ ...customerDraft, password: e.target.value })}
                  size="large"
                  type={isPasswordHidden ? 'password' : 'text'}
                  value={customerDraft.password || ''}
                  width="true"
                />
                <CButton
                  onClick={generatePassword}
                  type="primary"
                  xstyle={{
                    borderRadius: '12px',
                    margin: '16px 0px 0px 16px'
                  }}>
                  Generate
                </CButton>
              </div>
            )}
          </div>
        </form>
      </Modal>
    </div>
  );
};

export default CustomerDetailEditModal;

enter image description here

notice how metdata field is rendered? i want to use recursion to output every field which metadata contains, i know recursion but what i cant seem to figure out is where should this component call itself to do it.

any help with explanation so that i can understand the answer would be much appreciated!

this is the object im iterating on:

const customerData = {
  createdAt: "2022-10-28T08:42:08.015Z",
  email: "[email protected]",
  firstName: "$$$$$$$",
  fullName: "$$$$$$",
  idNumber: "2813921321",
  isEmailVerified: true,
  isPhoneVerified: true,
  lastName: "$$$$$",
  metadata: {
    birthDate: "2000-08-19 00:00:00.000",
    gender: "Male",,
    region: "",
    status: "Adult",
    statusExtra: "Student",
  },
  phone: "######",
  project: "hlkjhkljhkjhk",
  updatedAt: "2022-11-01T10:26:32.677Z",
  username: null,
  _id: "hlkjhlkjhlkjhlkjhlkjh",
};

see metadata? currently im outputting only the fields of the main(parent) object, but i also want to output the data which is contained in the 'metadata' key using recursion.

CodePudding user response:

A solution to this could be to check if the key item[0] is "metadata". Then you could do the same as you did with the customerDraft object. Get the entries an map over them.

Note that I destructured the array you get from the .entries to make it more explicit what the variables are.

if (item[0] === "metadata") {
  const inputs = Object.entries(item[1]).map(([metaKey, metaValue]) => (
    <CStandardInput
      key={metaKey}
      allowClear
      defaultValue={metaValue}
      disableUnderline
      formclasses={{ root: classes.root }}
      htmlFor={`standard-customer-edit-${metaKey}`}
      id={`standard-customer-edit-${metaKey}`}
      label={metaKey}
      onChange={(event) => {
        setDraftCustomer({
          ...customerDraft,
          fullName: event.target.value,
        });
        setDraftProfile({
          ...customerProfileDraft,
          fullName: event.target.value,
        });
      }}
      size="large"
    />
  ));
  return <>{inputs}</>;
}

return (
  <CStandardInput
  ...

EDIT:

To support the nested data with recursion, I've created a function with returns an input for every key, value pair in the data.

You can add your extra if statements as desired

const renderInputs = (data) => {
  const inputs = Object.entries(data).map(([key, value]) => {
    if (
      typeof value === "object" &&
      !Array.isArray(value) &&
      value !== null
    ) {
      return renderInputs(value);
    }

    return (
      <CStandardInput
        key={key}
        allowClear
        defaultValue={value}
        disableUnderline
        formclasses={{ root: classes.root }}
        htmlFor={`standard-customer-edit-${key}`}
        id={`standard-customer-edit-${key}`}
        label={key}
        onChange={(event) => {
          setDraftCustomer({
            ...customerDraft,
            fullName: event.target.value,
          });
          setDraftProfile({
            ...customerProfileDraft,
            fullName: event.target.value,
          });
        }}
        size="large"
      />
    );
  });
  return inputs;
};

return <>{renderInputs(customerData)}</>;

Hope this helps you with your project!

  • Related