2

I'm trying to use a redux store state value in an input, but I have a custom useInput hook, that I can't figure how to make them work together.

I built a react custom useInput hook that handles the value, and change/blur events. Used like:

const {
    value: titleValue,
    error: titleError,
    inputChangedHandler: titleChangedHandler,
    inputBlurHandler: titleBlurHandler,
    setValue: setTitleValue,
  } = useInput(validateTitle);

<input
   error={titleError ?? false}
   id="title"
   name="title"
   type="text"
   placeholder="Stream Title"
   autoComplete="off"
   value={titleValue}
   onChange={titleChangedHandler}
   onBlur={titleBlurHandler}
/>

my problem is that if I want to use it in an 'edit' components, which I want to fetch the initial values from an existing state, I cannot do it, because the input value is bound to the useInput value property.

so I can't do this, with my useInput custom hook:

const selectedItem = useSelector((state) => state.items.selectedItem);

<input
       error={titleError ?? false}
       id="title"
       name="title"
       type="text"
       placeholder="Stream Title"
       autoComplete="off"
       value={selectedItem.title} <-- use the state selectedItem value 
       onChange={titleChangedHandler}
       onBlur={titleBlurHandler}
    />

my useInput customer hook is just in charge of validation of the input value. It would work well if I could initially set the value to the store value, but my component is using useEffect to call an API getById(id) so the first time the component loads there is still no selectedItem, so I cannot initially set the useInput to the selectedItem.title.

this is my useInput custom hook code:

import { useState } from 'react';

const useInput = (validate) => {
  console.log('in useInput');
  const [value, setValue] = useState('');
  const [isTouched, setIsTouched] = useState(false);

  const validationResult = validate(value);
  const error = !validationResult.isValid && isTouched && validationResult.message;

  const inputChangedHandler = (event) => {
    setIsTouched(true);
    setValue(event.target.value);
  };

  const inputBlurHandler = () => {
    setIsTouched(true);
  };

  return { value, error, inputChangedHandler, inputBlurHandler, setValue };
};

export default useInput;

How can I fix it?

Roni Axelrad
  • 399
  • 3
  • 13
  • Can you update your question to include all relevant code? What is this `useInput` hook doing? How is it implemented? What is `validateTitle` that is passed to it? Do you just want to set the initial state of the input from your redux store, or do you need to persist the input changes also to the redux store? – Drew Reese Feb 25 '22 at 18:38
  • @DrewReese Hi Drew, thanks for your reply. I added the useInput code. It is only in charge of validation. The problem is probably hides on the fact that the component first 'selectedItem' is undefined. the component then call REST api to get the item by id, and only after it returns (through thunk), the store 'selectedItem' state is being populated. so I cannot initially set the useInput to the title of selectedItem. I hope it's clear enough. – Roni Axelrad Feb 25 '22 at 21:15
  • hi @RoniAxelrad what is validateTitle? It is some function you passes to your useInput to validate the title value you fetch from redux Store / api ? You want your useInput to recalculate values or perform operations on state Change? Currently it is not updating on state change on initial render? – Wasif Ali Feb 25 '22 at 21:29
  • @WasifAli Hi Wasif, the validateTitle is a function I pass when calling the useInput(), this function is validating the value of the title input tag on every onChange, so basically everytime a user change the input value, it will validate the validation function inside the useInput hook. Currently, the main problem is that 'titleValue' from useInput return object, is bound to the input tag 'value' but I need to set the value of currentItem (which is populated into the store, after an API call that happens after the first useEffect is running) – Roni Axelrad Feb 25 '22 at 23:01
  • I think you can pass the redux state from component to useInput hook along with validateTitle. and in useInput you can set the value. This way you can make your hook work fine. I am not sure if it works. i can write in answer for clearification if you want. – Wasif Ali Feb 25 '22 at 23:08
  • I tried that, but it doesn't work well, because the useState(value) in useInput is setting the value only the first time it is being called, and the value of the redux state is still not populated when the component first render, because the API is being called from useEffect, and that's only after the component is rendering first. that's basically the main issue. Maybe I cannot use my custom useInput hook in my case ? – Roni Axelrad Feb 25 '22 at 23:14
  • I think it is possible. I same for debouncing. Dont know what is issue. But I still believe that passing redux state will fix problem. const { value: titleValue, error: titleError, inputChangedHandler: titleChangedHandler, inputBlurHandler: titleBlurHandler, setValue: setTitleValue, } = useInput(validateTitle,state_fetched_from_useSelector); – Wasif Ali Feb 25 '22 at 23:33
  • I am doing same in my current project using rtk-query and it works perfect. – Wasif Ali Feb 25 '22 at 23:34
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/242424/discussion-between-wasif-ali-and-roni-axelrad). – Wasif Ali Feb 25 '22 at 23:35
  • @DrewReese Wasif Ali Actually, I think I found a solution in a different question that I couldn't locate before. https://stackoverflow.com/questions/60646482/how-can-i-set-default-value-after-actions-in-useeffect-when-i-use-custom-hooks – Roni Axelrad Feb 27 '22 at 00:26
  • If I add useEffect inside useInput with setValue(initialValue) in it, and add the initialValue as a dependency to that useEffect, the second time the component renders with the correct initialValue, will trigger the useEffect and set the correct value from the store – Roni Axelrad Feb 27 '22 at 00:27
  • 1
    Right, if your redux store is updated ***after*** the component renders then you'd need to use an `useEffect` hook to "listen" to changes to the store value to "synchronize" any local component state. – Drew Reese Feb 28 '22 at 22:17

0 Answers0