0

I have a component that contains a Checkbox component from the @fluentui/react-components library, and I am using an onChange event on the Checkbox component.

const ContactText = ({ contact, contactKey, selectedContacts, setSelectedContacts }) => {

  return (
    <div>
      <img src={contactIcon} alt="Contact Icon" />
      <div>
        <Checkbox
          labelPosition="before"
          label={`${contact.firstname} ${contact.surname}`}
          checked={selectedContacts.filter((contact) => contact.key == contactKey).checked}
          //@ts-ignore - Ignore event parameter on onChange
          onChange={(event, data) => {
            setSelectedContacts((prevState: SelectedContactModel[]) =>
              data.checked
                ? [
                    ...prevState,
                    {
                      key: contactKey,
                      checked: data.checked,
                      companyKey: contact.companyKey,
                      companyName: contact.companyName,
                      individual: contact.individual,
                      title: contact.title,
                      firstname: contact.firstname,
                      surname: contact.surname,
                      telephone: contact.telephone,
                      mobile: contact.mobile,
                      retired: contact.retired,
                      address: contact.address,
                      postcode: contact.postcode,
                      email: contact.email,
                    },
                  ]
                : prevState.filter((contact) => contact.key !== contactKey)
            );
          }}
        />
        {contact.email != "" ? <SecondaryText text={contact.email} /> : <></>}
        {contact.telephone != "" ? <TertiaryText text={contact.telephone} /> : <></>}
        {contact.mobile != "" ? <TertiaryText text={contact.mobile} /> : <></>}
      </div>
    </div>
  );
};

I want to have the onChange to happen when I click the outermost div of the ContactText component. I have seen people using useRef although I am wondering if there are different ways of doing this.

4
  • Look into useImperativeHandle Commented Jan 22, 2024 at 16:35
  • I saw this post: stackoverflow.com/questions/55456604/… Would this be a solution I could take? Commented Jan 22, 2024 at 16:50
  • That's exactly what you need. This answer (the updated with hooks portion) Commented Jan 22, 2024 at 17:25
  • Don't forget that you can also declare the onChange handler in your parent component and just pass it down as a prop to your ComtactText and then pass that to CheckBox. Commented Jan 22, 2024 at 17:29

3 Answers 3

1

Try this

const ContactText = React.forwardRef<{ onChange: (event: any, data: any) => void; scrollIntoViewOnClick: () => void }, HTMLDivElement>(({ contact, contactKey, selectedContacts, setSelectedContacts }, ref) => {
  React.useImperativeHandle((ref) => ({
    // You could name this whatever you want, we'll call it 'onChange'
    onChange: handleChange,
    // Just for the sake of this example and what 'useImperativeHandle' is used for
    scrollIntoViewOnClick: () => {
      // Logic to query for your div on the page and then scroll into view
      console.log('should scroll into view');
    }
  }));

  const handleChange = (event, data) => {
            setSelectedContacts((prevState: SelectedContactModel[]) =>
              data.checked
                ? [
                    ...prevState,
                    {
                      key: contactKey,
                      checked: data.checked,
                      companyKey: contact.companyKey,
                      companyName: contact.companyName,
                      individual: contact.individual,
                      title: contact.title,
                      firstname: contact.firstname,
                      surname: contact.surname,
                      telephone: contact.telephone,
                      mobile: contact.mobile,
                      retired: contact.retired,
                      address: contact.address,
                      postcode: contact.postcode,
                      email: contact.email,
                    },
                  ]
                : prevState.filter((contact) => contact.key !== contactKey)
            );
          };

  return (
    <div ref={ref}>
      <img src={contactIcon} alt="Contact Icon" />
      <div>
        <Checkbox
          labelPosition="before"
          label={`${contact.firstname} ${contact.surname}`}
          checked={selectedContacts.filter((contact) => contact.key == contactKey).checked}
          //@ts-ignore - Ignore event parameter on onChange
          onChange={handleChange}
        />
        {contact.email != "" ? <SecondaryText text={contact.email} /> : <></>}
        {contact.telephone != "" ? <TertiaryText text={contact.telephone} /> : <></>}
        {contact.mobile != "" ? <TertiaryText text={contact.mobile} /> : <></>}
      </div>
    </div>
  );
});

useImperativeHandle enables you to expose methods that only you want to be available outside of your component.

Sign up to request clarification or add additional context in comments.

Comments

0

You can either add a onClick etc on the div element and do what you do inside the onChange in the Checkbox (although you will need to compute checked like you do in the render), or you can just change the <div> into a <label> as clicking on a label will delegate the click to whichever <input> element it wraps (or has a for with a matching ID).

 const ContactText = ({ contact, contactKey, selectedContacts, setSelectedContacts }) => {
  return (
    <label> {/* <------------ */}
      <img src={contactIcon} alt="Contact Icon" />
      <div>
        <Checkbox
          labelPosition="before"
          label={`${contact.firstname} ${contact.surname}`}
          checked={selectedContacts.filter((contact) => contact.key == contactKey).checked}
          //@ts-ignore - Ignore event parameter on onChange
          onChange={(event, data) => {
            setSelectedContacts((prevState: SelectedContactModel[]) =>
              data.checked
                ? [
                    ...prevState,
                    {
                      key: contactKey,
                      checked: data.checked,
                      companyKey: contact.companyKey,
                      companyName: contact.companyName,
                      individual: contact.individual,
                      title: contact.title,
                      firstname: contact.firstname,
                      surname: contact.surname,
                      telephone: contact.telephone,
                      mobile: contact.mobile,
                      retired: contact.retired,
                      address: contact.address,
                      postcode: contact.postcode,
                      email: contact.email,
                    },
                  ]
                : prevState.filter((contact) => contact.key !== contactKey)
            );
          }}
        />
        {contact.email != "" ? <SecondaryText text={contact.email} /> : <></>}
        {contact.telephone != "" ? <TertiaryText text={contact.telephone} /> : <></>}
        {contact.mobile != "" ? <TertiaryText text={contact.mobile} /> : <></>}
      </div>
    </label>
  );
};

Comments

0

Based on @Mike K's answer and another answer I found, this is what I came up with. This lets me call onChange from the div element outside of the Checkbox element.

const ContactText = React.forwardRef(
  ({ contact, contactKey, selectedContacts, setSelectedContacts }: any, ref: any) => {
    const [checked, setChecked] = useState(false);

    React.useImperativeHandle(
      ref,
      () => {
        return {
          onChange: handleChange,
        };
      },
      []
    );

    // @ts-ignore: next-line - Ignore event parameter on onChange
    const handleChange = (event, data) => {
      setChecked(!checked);

      setSelectedContacts((prevState: SelectedContactModel[]) =>
        data.checked
          ? [
              ...prevState,
              {
                key: contactKey,
                checked: data.checked,
                companyKey: contact.companyKey,
                companyName: contact.companyName,
                individual: contact.individual,
                title: contact.title,
                firstname: contact.firstname,
                surname: contact.surname,
                telephone: contact.telephone,
                mobile: contact.mobile,
                retired: contact.retired,
                address: contact.address,
                postcode: contact.postcode,
                email: contact.email,
              },
            ]
          : prevState.filter((contact) => contact.key !== contactKey)
      );
    };

    return (
      <div ref={ref}>
        <img src={contactIcon} alt="Contact Icon" />
        <div>
          <Checkbox
            labelPosition="after"
            label={`${contact.firstname} ${contact.surname}`}
            checked={selectedContacts.filter((contact: SelectedContactModel) => contact.key === contactKey).checked}
            onChange={handleChange}
          />
          {contact.email != "" ? <SecondaryText text={contact.email} /> : <></>}
          {contact.telephone != "" ? <TertiaryText text={contact.telephone} /> : <></>}
          {contact.mobile != "" ? <TertiaryText text={contact.mobile} /> : <></>}
        </div>
      </div>
    );
  }
);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.