Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

239
Visualizações
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?

about 4 years ago · Juan Pablo Isaza
1 Respostas
Responde à pergunta

0

I solved the issue by exporting an initGalleryData function from GalleryComp and using it in the form. This way, details are hidden in the child.

This way, I can reuse the Gallery Component with any object, by simply not forgetting to initiate the object and by providing an 'onDataChange' modificator

This code is used in the weally.org project, gathering people for protests online by concens

export default function GalleryComp(props) {
    const {issue, onItemsChanged} = props
    const attachmentsMeta = issue.attachmentsMeta

    const {t} = useTranslation('complaintList')


    function setMainAttachmentIndex(index) {
        const attachmentsMetaCopy = {...attachmentsMeta}
        attachmentsMetaCopy.mainAttachmentIndex = index
        onItemsChanged({attachmentsMeta:attachmentsMetaCopy})
    }

    function onAttachmentRemoveAction(attachmentId, removed) {
        const attachmentsModified = {...attachmentsMeta}
        let attr = attachmentsModified.attachmentsAttributes[attachmentId]
        if (!attr) {
            attr = {deleted: removed}
            attachmentsModified.attachmentsAttributes[attachmentId] = attr
        } else {
            attr.deleted = removed
        }
        onItemsChanged({attachmentsMeta: attachmentsModified})
    }

    if (attachmentsMeta.images?.length === 0)
        return <Divider style={sx.descDivider}/>

    return (
        <>
            <Gallery attachments={attachmentsMeta} onRemoveAction={onAttachmentRemoveAction}
                     selectedIndex={attachmentsMeta.mainAttachmentIndex}
                     onSelectionChange={setMainAttachmentIndex}/>
            <Divider style={sx.descDivider}/>
        </>
    )

}

export function initGalleryData(issue) {
    issue.attachmentsMeta = {
        images: [...issue.attachments],
        attachmentsAttributes: {},
        mainAttachmentIndex: 0
    }
}

I then just have to initialize the needed fields at component creation.

export default function IssueFieldsForm(props) {
    const {t} = useTranslation('complaintList')
    const {
        issue,
        onIssueDataChanged,
        markers,
        onMarkersChanged,
        onMarkerSelected,
        setPlaceMarkersDisplayed,
        placeMarkersDisplayed
    } = props

    initGalleryData(issue);
    const [validationState, setValidationState] = useState(new ValidationResult())
//...
return (
        <Card sx={sx.issueFieldsForm} id="IssueFieldsForm">
            <Grid item sx={sx.item} ref={scrollTopRef}>
                <Typography variant={'h5'} sx={sx.header}>{t('edit.header')}</Typography>
            </Grid>
//...
<GalleryComp issue={issue} onItemsChanged={onIssueDataChanged}/>
//...
)
about 4 years ago · Juan Pablo Isaza Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda