Wiskuxgodx
Wiskuxgodx

Reputation: 25

How to update custom hook value

I've started learning typescript and react hooks on my new web project. I have some problem with managing initial value of my custom hook i've made. Cant step over this functionality. How should I update nameInput hook, after data is fetched from my API. For example if I pass custom string as initial value in useTextInput everything is fine, but problem is when im passing undefined first there(when data is fetched from API) and value after is downloaded, and seems like it is not updating.

My question is, how to update in AccountInput inputValue value properly by value coming from API as userAccount.firstName from getUser() method, witch depends to nameInput, witch is not updated after initial value in useTextInput.

Account.tsx

import React, { useEffect, useState} from 'react';
import { connect } from 'react-redux';
import { IApplicationState } from '../../store';
import { actionCreators, reducer, AccountStatusEnum } from '../../store/Account';
import { MDBBtn, MDBInput } from 'mdbreact';
import { AccountInput } from './child-components';
import { useTextInput } from '../../hooks';

type AccountProps = ReturnType<typeof reducer> & typeof actionCreators;

const Account: React.FC<AccountProps> = ({ getUser, resetState, userAccount, status }) => {
    const nameInput = useTextInput(userAccount.firstName);
    const [isInputInvalid, setIsInputInvalid] = useState<boolean>(false);
    useEffect(() => {
        getUser();
    }, []);

        return (
            <React.Fragment>
                <div className="mt-3 container border border-light">
                    <h5 className="secondary-heading font-weight-bold pt-3 pl-5">User info</h5>
                    <div className="row">
                        <AccountInput
                            textInput={nameInput}
                            typeInput="text"
                            labelInput="Your name & surename"
                            isInputInvalid={isInputInvalid}
                        />
                    </div>
                </div>
            </React.Fragment>
        );
};

const mapStateToProps = (state: IApplicationState) => state.acc;

export default connect(mapStateToProps, actionCreators)(Account as any);

AccointInput.tsx

import React, { Fragment, useEffect, useState } from 'react';
import { TextInput } from '../../../hooks';
import { MDBInput } from 'mdbreact';

type AccountInputProps = {
    readonly textInput: TextInput;
    readonly isInputInvalid: boolean;
    readonly typeInput: string;
    readonly labelInput: string;
};

const AccountInput = React.memo<AccountInputProps>(({ textInput, isInputInvalid, typeInput, labelInput }) => {
    const { hasValue, bindToInput } = textInput;
    return (
        <Fragment>
            <div className="col-sm w-100 px-5">
                <MDBInput hint={textInput.value} {...bindToInput} type={typeInput} label={labelInput} />
            </div>
        </Fragment>
    );

});

AccountInput.displayName = 'AccountInput';

export default AccountInput;

useTextInput.ts

import { useState, useCallback, useMemo, FormEvent } from 'react';

export type TextInputType = 'text' | 'password';

export type TextInput = {
    value: string;
    hasValue: boolean;
    clear: () => void;
    bindToInput: {
        value: string;
        type: TextInputType;
        onChange: (e: FormEvent<HTMLInputElement>) => void;
    };
};

export const useTextInput = (initial: string = '', type: TextInputType = 'text'): TextInput => {
    const [value, setValue] = useState<string>(initial);
    const clear = useCallback((): void => setValue(''), []);
    const onChange = useCallback((e: FormEvent<HTMLInputElement>): void => setValue(e.currentTarget.value), []);

    return useMemo<TextInput>(
        () => ({
            value,
            clear,
            hasValue: !!(value && value.trim()),
            bindToInput: {
                type,
                value,
                onChange,
            },
        }),
        [value, type, onChange, clear],
    );
};

Upvotes: 1

Views: 4064

Answers (1)

Lu&#239;s
Lu&#239;s

Reputation: 2843

I don't know for sure how custom hooks handle promises, but normally when you want to pass an async value to useState you do something like this:

const useTextInput = (initial) => {
  const [value, setValue] = useState(initial);

  useEffect(() => {
    setValue(initial);
  }, [initial]);
};

You can use useEffect to update your stateful value when initial changes. Would this work in your case?

Upvotes: 2

Related Questions