I’m currently having trouble with the phone component I’m writing. To be extra vague about it, on my onChange, I’m using a function to move to the next text box. However, when I use my data handler function to raise the value to the parent, it doesn’t work. In addition, by adding my data handler to the individual text boxes, it breaks my next Box function.

//dataHandler => raises state data to parent
//nextBox => moves focus to next text box 
 
//onChange should be here
 {
    dataHandler(dataProp, e); 
    nextBox(1);
} />

In the code above, either nextBox works alone, or if I add my handler, then neither work.

I’m also wondering about practices. On other forms, I put this type of handler on single boxes, and it works fine. Since my phone input component has multiple boxes, I’m thinking that onChange won’t work exactly the same regardless.

Any advice, tips, or need to know info?

Ps: I’m on mobile, so I greatly simplified the code I’m using, and the formatting is wonky. Assume that there’s an onChange before the brackets with an (e) =>. My app or lemmy is deleting it on submission.

  • Oliver Lowe
    link
    29 months ago

    Hard to troubleshoot without a fuller example.

    when I use my data handler function to raise the value to the parent, it doesn’t work.

    What doesn’t work? What is happening? Is the function returning some unexpected value? Some exception being thrown? Does some other component get in an unexpected state?

    Hopefully we can help out with some more context :)

    • DecideOP
      link
      fedilink
      2
      edit-2
      9 months ago

      Hey, I’m just now seeing this. So, my component hierarchy is something like this:

      App

      • Form
        • TextInput
        • PhoneInput

      The TextInput components are very simple:

      import { ErrorMessage } from "../ErrorMessage"; //this function can be used to determine if the error message renders based on criteria
      
      export const FunctionalTextInput = ({
        dataProperty,
        errorMessage,
        placeholder,
        value,
        propertyHandler,
      }: {
        dataProperty: string;
        errorMessage: string;
        placeholder: string;
        value: string;
        propertyHandler: (property: string, e: string) => void;
      }) => {
        //Object.keys(initialUserData)[0]
        return (
          <>
            <div>
              {dataProperty}:
               propertyHandler(dataProperty, e.target.value)}
              />
            </div>
            
          
        );
      };
      
      export const FunctionalTextInput = ({
        dataProperty,
        errorMessage,
        placeholder,
        value,
        propertyHandler,
      }: {
        dataProperty: string;
        errorMessage: string;
        placeholder: string;
        value: string;
        propertyHandler: (property: string, e: string) => void;
      }) => {
        //Object.keys(initialUserData)[0]
        return (
          &lt;>
            <div>
              {dataProperty}:
               propertyHandler(dataProperty, e.target.value)}
              />
            </div>
            
          
        );
      };
      

      The shape of my data is like so:

      export type UserInformation = {
        firstName: string;
        lastName: string;
        email: string;
        city: string;
        phone: string[];
      };
      

      In my Form Component, I have two functions that work in the TextInput component, but not the PhoneInput component.

      const dataHandler = (e: FormEvent) => {
          e.preventDefault();
          userDataHandler(formData);
          setFormData(initialUserData);
        };
      const propertyHandler = (property: string, value: string) => {
          setFormData((prevProp) => ({ ...prevProp, [property]: value }));
        };
      

      So, over the past few hours I’ve been trying to talk to bing about this, and get some answers. After a few hours, I finally think the problem is a conflict of state. It seems like the state I’m using in my PhoneInput component interferes with the state of the parent component. This seems to be the case since when I click submit, my dataHandler function doesn’t trigger for the PhoneInput component.

      So, I guess now I’m wondering how that works? I’ve heard of raising state to the parent, but passing state down, not as data, but as actual state, sounds difficult and somewhat complex. I’m wondering how to use this technique, the uses, and how I can determine when to use it. Or, better yet, maybe I’m missing something and the answer is right outside my reach.

      The phone input in question:

      // This is a component that is used for the phone input
      // it wall accept 4 inputs, and "lift" the values to the parent component as a single, unformatted string.
      
      import { ChangeEventHandler, useRef, useState } from "react";
      import { ErrorMessage } from "../ErrorMessage";
      
      type TPhoneInputProps = {
        errorMessage: string;
        dataProperty: string;
        higherPhoneState: string[];
        propertyHandler: (property: string, e: string) => void;
      };
      export const FunctionalPhoneInput = ({
        errorMessage,
        dataProperty,
        higherPhoneState,
        propertyHandler,
      }: TPhoneInputProps) => {
        const [phoneState, setPhoneState] = useState(["", "", "", ""]);
        const phoneNumber = [
          useRef(null),
          useRef(null),
          useRef(null),
          useRef(null),
        ];
        const phoneNum0 = phoneNumber[0];
        const phoneNum1 = phoneNumber[1];
        const phoneNum2 = phoneNumber[2];
        const phoneNum3 = phoneNumber[3];
        const phoneChangeController =
          (
            index: 0 | 1 | 2 | 3 //  1 | 2 | 3 | 4,
          ): ChangeEventHandler =>
          (e: React.ChangeEvent) => {
            const length = [2, 2, 2, 1];
            const nextInput = phoneNumber[index + 1];
            const prevInput = phoneNumber[index - 1];
            const maxLength = length[index];
            const value = e.target.value;
            const shouldGoToNextInput =
              maxLength === value.length &amp;&amp; nextInput?.current;
            const shouldGoToPrevInput = value.length === 0;
            const newState = phoneState.map((phone, phoneIndex) =>
              index === phoneIndex ? e.target.value : phone
            );
            if (shouldGoToNextInput) {
              nextInput.current?.focus();
            }
            if (shouldGoToPrevInput) {
              prevInput.current?.focus();
            }
            setPhoneState(newState);
            console.log(newState.join(""));
            console.log(dataProperty);
      
            // Concatenate the new state with e.target.value to get the full phone number
            // const fullPhoneNumber =
            //   newState.slice(0, index).join("") +
            //   e.target.value +
            //   newState.slice(index + 1).join("");
            propertyHandler(dataProperty, newState.join(""));
          };
        return (
      &lt;> 
      <div>
              Phone:
              <div>
                
                -
                
                -
                
                -
                
              </div>
            </div>
      
        );
      };
      

      Please note that this component is 1000% broken. I was in the process of changing it with Bings suggestions, but it’s frustrating getting anything useful out of the thing.

    • DecideOP
      link
      fedilink
      19 months ago

      If there’s any links, resources, mental models, or anything that you or anyone else think would be helpful in getting this to work, I’m all ears. Also, since it’s pretty obvious that this is an assignment, my limitation is that I cannot use useEffect, and the PhoneComponent has to use 4 inputs.

      I’ve been stuck on this for about a week now, so any help, feedback, insight, or articles I should read would be incredibly appreciated.

      • Oliver Lowe
        link
        2
        edit-2
        9 months ago

        Thanks! Some quick thoughts.

        Recommend staying away from LLMs (Bing, ChatGPT et al.) for now.

        Take a look at Sharing data between components from the official React quick start guide.

        Try thinking about what is required without thinking about React. I think we need 4 input elements. The value of those elements needs to be concatenated and displayed to the user. Is that right?

        From there, we can work out a way to implement what we need using React. Thinking this way helps us start with an even simpler implementation e.g. using just 1 input element instead of 4.

        There’s a few things we don’t need right now: unused props like errorMessage, useRef(), and changing focus.

        I’ve heard of raising state to the parent, but passing state down, not as data, but as actual state, sounds difficult and somewhat complex.

        I think what you’re talking about are Signals from Preact? This feature isn’t required right now and it introduces complexity; I personally find it hard to juggle extra concepts in my head especially when starting out with some new tooling.

        If you’re still interested we could try writing a couple of basic components that get us part of the way there.

        • DecideOP
          link
          fedilink
          1
          edit-2
          9 months ago

          I’ll stay away from the LLMs for now – they’re largely unhelpful anyway.

          The value of those elements needs to be concatenated and displayed to the user. Is that right?

          Right. The other components with a single text box seem to work fine, it’s the multiple boxes in the phone component, and it’s local state, that are confusing me to no end.

          I think what you’re talking about are Signals from Preact?

          Oh, no, when I was talking to Bing it said that the local state interferes with the parent state, so I instead need to bring the parent state into the child. It sort of makes sense? At least the part that local and parent state can interfere with each other makes sense.

          If you’re still interested we could try writing a couple of basic components that get us part of the way there.

          Please, I’m all ears. I have a feeling that your approach will help me a lot.

          • Oliver Lowe
            link
            2
            edit-2
            9 months ago

            Ok! Just so I’m clear on what we need to do, what do you think the final HTML would look like? Happy to keep comms over Lemmy but email might be easier than fighting with Lemmy’s markdown renderer. My deets are at http://www.olowe.co/about.html in “Contact”

            • DecideOP
              link
              fedilink
              1
              edit-2
              9 months ago

              For sure, I’ll send you an email.

              edit: sent. Thank you again for the help.