Search code examples
reactjsmeteorreact-hooks

Cannot access ___ before initialization reactjs useState useTracker Subscriptions Form state meteor


I have a form that takes its state from a react useState hook, that hooks default value I would like to come from a useTracker call, I am using pub sub in Meteor to do this. I get a error Cannot access '' before initialization I know it has something to do with the lead not being ready yet and returning undefined and the hook not being able to use that, at least I think so. But I am not sure how to solve that.

Here is my code thus far

import React, { useState } from "react";
import Dasboard from "./Dashboard";
import { Container } from "../styles/Main";
import { LeadsCollection } from "../../api/LeadsCollection";
import { LeadWalkin } from "../leads/LeadWalkin";
import { useTracker } from "meteor/react-meteor-data";

const Walkin = ({ params }) => {
  const [email, setEmail] = useState(leads.email);

  const handleSubmit = (e) => {
    e.preventDefault();

    if (!email) return;

    Meteor.call("leads.update", email, function (error, result) {
      console.log(result);
      console.log(error);
    });

    setEmail("");
  };

  const { leads, isLoading } = useTracker(() => {
    const noDataAvailable = { leads: [] };

    if (!Meteor.user()) {
      return noDataAvailable;
    }

    const handler = Meteor.subscribe("leads");

    if (!handler.ready()) {
      return { ...noDataAvailable, isLoading: true };
    }

    const leads = LeadsCollection.findOne({ _id: params._id });
    return { leads };
  });

  console.log(leads);

  //console.log(params._id);

  const deleteLead = ({ _id }) => {
    Meteor.call("leads.remove", _id);
    window.location.pathname = `/walkin`;
  };
  return (
    <Container>
      <Dasboard />
      <main className="split">
        <div>
          <h1>Edit a lead below</h1>
        </div>
        {isLoading ? (
          <div className="loading">loading...</div>
        ) : (
          <>
            <LeadWalkin
              key={params._id}
              lead={leads}
              onDeleteClick={deleteLead}
            />
            <form className="lead-form" onSubmit={handleSubmit}>
              <input
                type="text"
                value={email}
                onChange={(e) => setEmail(e.target.value)}
                placeholder="Type to edit lead"
              />

              <button type="submit">Edit Lead</button>
            </form>
          </>
        )}
      </main>
    </Container>
  );
};

export default Walkin;

Solution

  • It should work if you change the order of these two hooks, but it's probably better to break this into two components so that you can wait until your subscription is ready before you try to use leads.email as default value. It's not possible to branch out ('return loading`) in between hooks, because React doesn't like it when the number of hooks it finds in a component change in-between re-renderings.

    const Walkin = ({ params }) => {
      const { leads, isLoading } = useTracker(() => {
        const noDataAvailable = { leads: [] };
    
        if (!Meteor.user()) {
          return noDataAvailable;
        }
    
        const handler = Meteor.subscribe("leads");
    
        if (!handler.ready()) {
          return { ...noDataAvailable, isLoading: true };
        }
    
        const leads = LeadsCollection.findOne({ _id: params._id });
        return { leads };
      });
    
      if (isLoading || !leads) {
        return <div>loading..</div>;
      } else {
        return <SubWalkin params=params leads=leads />;
      }
    };
    
    const SubWalkin = ({ params, leads }) => {
      const [email, setEmail] = useState(leads.email);
    
      ...
    };