1

Using Fluent UI React, I'm displaying some data from an AppSync API in a TextField. I want to be able to show text from the API for a contact form. I then want to edit that text and click a button to post it back to the AppSync API.

If I use the TextField component on its own, I can then use a hook to set a variable to result of an AppSync API call and then have the TextField component read the value coming from the variable I set with the hook. I can then edit that text as I feel like and its fine.

The problem I have is that if I want to take edits to the TextField and set them using my hook I lose focus on the TextField. To do this I am using the onChange property of TextField. I can set the variable fine but I have to keep clicking back in to the input window.

Any thoughts on how I can keep the focus?

import React, { useEffect, useState } from 'react';
import { API, graphqlOperation } from 'aws-amplify';
import * as queries from '../../graphql/queries';
import { Fabric, TextField, Stack } from '@fluentui/react';

const PhoneEntryFromRouter = ({
  match: {
    params: { phoneBookId },
  },
}) => PhoneEntry(phoneBookId);

function PhoneEntry(phoneBookId) {
  const [item, setItem] = useState({});

  useEffect(() => {
    async function fetchData() {
      try {
        const response = await API.graphql(
          graphqlOperation(queries.getPhoneBookEntry, { id: phoneBookId })
        );
        setItem(response.data.getPhoneBookEntry);
      } catch (err) {
        console.log(
          'Unfortuantely there was an error in getting the data: ' +
            JSON.stringify(err)
        );
        console.log(err);
      }
    }
    fetchData();
  }, [phoneBookId]);

  const handleChange = (e, value) => {
    setItem({ ...item, surname: value });
  };

  const ContactCard = () => {
    return (
      <Fabric>
        <Stack>
          <Stack>
            <TextField
              label='name'
              required
              value={item.surname}
              onChange={handleChange}
            />
          </Stack>
        </Stack>
      </Fabric>
    );
  };

  if (!item) {
    return <div>Sorry, but that log was not found</div>;
  }

  return (
    <div>
      <ContactCard />
    </div>
  );
}

export default PhoneEntryFromRouter;

EDIT

I have changed the handleChange function to make use of prevItem. For this event it does accept the event and a value. If you log that value out it is the current value and seems valid.

Despite the change I am still seeing the loss of focus meaning I can only make a one key stroke edit each time.

    setItem((prevItem) => {
      return { ...prevItem, surname: e.target.value };
    });
  };```

Adam
  • 350
  • 1
  • 5
  • 15

1 Answers1

2

I think you want the event.target's value:

const handleChange = e => {
    setItem(prevItem => { ...prevItem, surname: e.target.value });
};

You should also notice that in your version of handleChange(), value is undefined (only the event e is being passed as a parameter).

Edit: Now I see that you're setting the value item with data from a fetch response on component mount. Still, the value of item.surname is initially undefined, so I would consider adding a conditional in the value of the <TextField /> component:

value={item.surname || ''}

Ryan
  • 364
  • 2
  • 7
  • You're right I should make use of prevState. I have changed the handleChange code now I'll edit above – Adam Aug 29 '20 at 17:03
  • 1
    @Adam, and you're using `e.target.value` now? – Ryan Aug 29 '20 at 18:28
  • Yes if you see the edit above. The value is passed through, it sets the item variable and and is then read by the textfield. The problem I have is that the focus is lost so.I have to keep clicking back in to edit the field. – Adam Aug 29 '20 at 18:52
  • 1
    @Adam Yes, I understand, and I've updated my answer with another improvement. Now, I need to know if the `PhoneEntry` component is being re-rendered when typing. Of course, a sandbox would be great at this point. – Ryan Aug 29 '20 at 19:05
  • I think that's it. If put a console log at the top it prints on every keystroke in the textfield. Maybe I have to not think about it like an ordinary react text field. – Adam Aug 29 '20 at 19:25
  • I think I'll try memoising item or converting to a class component. – Adam Aug 29 '20 at 19:32
  • @Adam, The re-rendering is normal, but it shouldn't cause the loss of focus. Definitely something strange going on. Perhaps inside of the TextField. At least we improved some things in the process. – Ryan Aug 29 '20 at 19:38