0

Parent: (The TextField component is a styled input element from office-ui-fabric-react: Link)

<Searchbar>
    <TextField label="Account ID" />
    <TextField label="Account Name" />
</Searchbar>

Searchbar component:

export default class Searchbar extends React.Component<ISearchbarProps, ISearchbarState> {

    private _searchfields: React.ReactNode[];

    public constructor(props: ISearchbarProps) {

        super(props);
        this.state = {
            searchValues: {},
        };

        this._handleChange = this._handleChange.bind(this);
        this._getChild = this._getChild.bind(this);
        this._getChild();
        
    }

    private _handleChange(event: any, field: string) {
        const searchValues = this.state.searchValues;
        searchValues[field] = event.target.value;
        this.setState({ searchValues });
        console.log(this.state.searchValues);
    }

    private _getChild(): void {

        let fielCounter = 0;

        this._searchfields = React.Children.map(this.props.children, (child: any) => {
            if (child.type.displayName === 'StyledTextFieldBase') {

                const searchValues = this.state.searchValues;
                const fieldname = child.props.label.replace(/\s/g, "") + fielCounter++;
                searchValues[fieldname] = "";
                this.setState({ searchValues });

                return React.cloneElement(child, {
                    value: this.state.searchValues[fieldname], 
                    onChange: (e) => this._handleChange(e, fieldname)
                });
            }
        });
    }

    public render(): React.ReactElement<ISearchbarProps> {

        return (
            <div>{this._searchfields}</div>
        );

    }

}

The Problem:

When I type something into the input element, the state console output in the _handleChange shows the updated state, but the input value stays the same as I wouldn't type anything.

Also each letter gets remove from the state. So if I type "test", the console would output "t", "e", "s", "t"

What am I doing wrong?

Grobanix
  • 139
  • 1
  • 3
  • 12

2 Answers2

0

Here is an example of how to use correctly onChange event handlers. https://sebhastian.com/react-onchange/.
TextField is implemented on top of basic input, so you should be able to use the same principals. The problem that you have is resulting from rendering the component each time you type something. Here you can find good explanation regarding this. In React ES6, why does the input field lose focus after typing a character?

  • But rerendering the component should let me loose focus and this is not happening. I don't see anything showing me the component is rerendering. – Grobanix Sep 17 '22 at 07:20
0

So I dont know how you managed to get it to build at all. Setting the state in the constructor is not working and throwing an error. But I guess your main problem is, that you build your searchfields only in constructor. It will never called again (unless the searchbar component is destroyed and rebuilded) and so your children are fixed.


    public constructor(props: ISearchbarProps) {

        super(props);
        this.state = {
            searchValues: {},
        };

        this._handleChange = this._handleChange.bind(this);
        this._getChild = this._getChild.bind(this);
        this._getChild();
        
    }

    private _handleChange(event: any, field: string) {
        const searchValues = this.state.searchValues;
        searchValues[field] = event.target.value;
        this.setState({ searchValues });
        console.log(this.state.searchValues);
    }

    private _getChild(): React.ReactNode[] | undefined | null {

        return React.Children.map(this.props.children, (child: any) => {

            if (child.type === 'input') {
                const fieldname = child.props.name;
                const value = this.state.searchValues[fieldname];

                return React.cloneElement(child, {
                    value: value, 
                    onChange: (e: any) => this._handleChange(e, fieldname)
                }) as React.ReactNode;
            }
        });
    }

    public render(): React.ReactElement<ISearchbarProps> {

        return (
            <div>{this._getChild()}</div>
        );

    }

}

I changed the code a bit to work with normal inputs but it should work with Textfields too. Now the getChild method is called everytime you type into the input. I am not sure if this is good in regards to performance. Maybe you can save the searchvalue state in an upper component (where you render the textfields) and declare them directly in the code. But I dont know your usecase here and why you are doing it like that in the first place.

<Searchbar searchValues={this.state.searchValues}>
        <input name="test1" value={this.state.searchValues["test1"]} onChange={...}/>
        <input name="test2"value={this.state.searchValues["test2"]} onChange={...}/>
</Searchbar>
devil_inside
  • 412
  • 3
  • 9
  • Maybe because spfx uses the old react version 16.13.1? Don't know but building it was not a problem. But this definitly fixed my problem. thanks! – Grobanix Sep 19 '22 at 08:32