mercredi 25 mai 2022

How to correctly isolate states in React

I want my parent component state to be updated by its children independently, without the parent doing micromanagement and being aware of variables modified by its children. I'm abstracting these modifications with a single method onIssueDataChanged. The modified state object will then be transferred to GraphQL, so no need for the parent to know what's in it. My problem, is that children cannot call onIssueDataChanged in useEffect.

Here's the concrete problem:

I have a form IssueFieldsForm that handles the saving of an object called issue.

The object has a gallery of pictures included, the pictures can be added, removed or selected as the main issue picture. That logic is isolated in a separate component called GalleryComp, and the info is stored in a non-persistent issue field called attachmentsMeta.

My problem is that I want to isolate handling of the attachmentsMeta in GalleryComp and have in IssueFieldsForm a generic method I called onIssueDataChanged which takes only changed values and updates the issue variable state with it. That seems hard to do since I initialize an attachmentsMeta state in GalleryComp with the current attachments in the useEffect hook, but I cannot send that back to the issue state object in the parent.

export default function GalleryComp(props) {
    const {issue, onItemsChanged} = props
    const [attachmentsMeta, setAttachmentsMeta] = useState({
        images: [],
        attachmentsAttributes: {},
        mainAttachmentIndex: 0
    })

    useEffect(() => {
        const newAttachmentsMeta = {...attachmentsMeta}
        newAttachmentsMeta.images = []
        logger.debug('attachments cont before concat: ', newAttachmentsMeta.images.length)
        newAttachmentsMeta.images = unionBy(issue.attachments, issue.attachmentsMeta?.images, 'url')
        logger.debug('after concat: ', newAttachmentsMeta.images.length)
        newAttachmentsMeta.mainAttachmentIndex = issue.mainAttachmentIndex
        logger.debug('displaying attachments: ', newAttachmentsMeta)

        setAttachmentsMeta(newAttachmentsMeta)
    }, [issue?.attachmentsMeta])

That is a problem for me because if I don't do any change in the attachments, onIssueDataChanged will never be called to update the variable issue.attachmentsMeta with the initial attachments. Leading me to lose my original attachments on save (since they were not transferred to my single source of truth issue.attachmentsMeta).

On the other hand, if I initialize issue.attachmentsMeta in the parent component, I lose the elegance of having each component handle its own variables.

p.s: Obviously I cannot call onIssueDataChanged in the effect since it would trigger a state change and enter an infinite loop.

How would you do this?

Aucun commentaire:

Enregistrer un commentaire