Migrating from Blaze to React - noob question

Hi, people.

I’m really struggling with this. Blaze was perfect for me but I need to migrate to React now.

This is simple thing but I don’t know how to do it with React. Basically, I just want to populate “fields” constant with reactive data from collection after subscription is ready and then pass it to a component as props.

In Blaze this was simple, I would use onCreated, onRendered and autorun-s, but in React I had no luck. Should I use “useEffect” or just use “useTracker”, I have no idea.

const [fields, setFields] = useState([])

const { petition, isLoading } = useTracker(() => {

    const noDataAvailable = { petition: {} };

    const handler = Meteor.subscribe('petitionSingle', addressId)

    if (!handler.ready()) {

        return { ...noDataAvailable, isLoading: true };

    }

    const petition = Petitions.findOne();

    return { petition };

})

const setData = useTracker(() => {

    setFields(petition.fields)

}, [fields])

useEffect(() => {

    console.log(fields)

}, [petition])

This one

const setData = useTracker(() => {
    setFields(petition.fields)
}, [fields])

Should be

const setData = useTracker(() => {
    setFields(petition.fields)
}, [petition.fields])

But the petition var is reactive already (because it comes from useTracker) then you don’t need to store the fields in a react state variable. If the petition changed, it will trigger the react component re-render.

I need reactive state because I’m passing data from fields to form inputs as values.

@minhna , I tried this:

const setData = useTracker(() => {
    setFields(petition.fields)
}, [petition.fields])

but it creates infinite loop renders

Hi, if you’re migrating to React and using forms, you might want to consider Uniforms, which is a very nice library for building forms with React.

I made a simple example system for my students that combines together Meteor, React, and Uniforms which provides sample code:

Good luck! My research project, RadGrad, also transitioned from Blaze to React several years ago. While it was bumpy at first, we ultimately found that React is a superior approach to building “composable” user interfaces: we got rid of a significant amount of code duplication by going to React.

You need to use useEffect on the updated petition fields and not useTracker

The first useTracker will reactively update the fields (meteor-wise), and useEffect will call setFields once the fields are updated

const [fields, setFields] = useState([])

const { petition, isLoading } = useTracker(() => {

    const noDataAvailable = { petition: {} };

    const handler = Meteor.subscribe('petitionSingle', addressId)

    if (!handler.ready()) {

        return { ...noDataAvailable, isLoading: true };

    }

    const petition = Petitions.findOne();

    return { petition };

})

useEffect(() => {

    setFields(() => petition.fields)

}, [isLoading])

I made it work with [isLoading] in useEffect. It’s ok now but I’m curious why when I try with “petition” instead, it creates infinite re-rendes:

    useEffect(() => {

        setFields(() => petition.fields)
        
    }, [petition])

Dependency array does not support deep comparison on an object. It only uses ===

I’m only guessing this is the case, but if you use the object petition as the key to watch to trigger a re-render, that is an object. When you check if an object === another object it doesn’t check the attributes, it just checks whether it is actually the same object. So during a check it would likely always be a different object that is being compared even if the attributes of that object are all the same.

const x = { a: 5 }
const y = { a: 5 }
console.log(x === y); // false

This is why you either need to put all the individual attributes into the array; or use something like isLoading like you did.