2

this is my code...

    class Module extends Component {
        constructor() {
            super()

            this.state = {
                inputs: [
                    { type: 'text', placeholder: 'placeholder text', name: 'text1', id: 'text1', value: 'aaa' },
                    { type: 'text', placeholder: 'another placeholder text', name: 'text2', id: 'text2', value: '' },
                    { type: 'text', placeholder: 'third placeholder text', name: 'text3', id: 'text3', value: '' },
                ]
            }

            this.handleInputChange = this.handleInputChange.bind(this)
            this.saveModule = this.saveModule.bind(this)
        }

        handleInputChange(event) {
            this.setState ({
                [event.target.name]: event.target.value
            })
        }

        renderInput = (input) => {
            return(
                <div key={ input.id }>
                    <input
                        type={ input.type }
                        name={ input.name }
                        placeholder={ input.placeholder }
                        onBlur={ this.saveModule }
                        value={ input.value }
                        onChange={ this.handleInputChange }
                    />
                </div>
            )
        }

        render() {
            return (
                <div>
                    { this.state.inputs.map(this.renderInput) }
                </div>
            )
        }
    }

    export default Module

How can i handle change of input which value is rendered from state in this way?! If i had {this.state.input.value} it works perfectly fine, once I refactor it like this, the setState doesn't seem to reach it anymore.

Any ideas? :)

Thanks a lot in advance!

1
  • first you must find the index of the object you will work on. so consider event.target.name as the condition to filter the array. then create a new array with the changes. and set the state. heres a good way how to do it stackoverflow.com/a/36010124/7744070 Commented Mar 28, 2017 at 9:46

2 Answers 2

2

Since you want to do the changes directly in input array from where you are creating the input elements, so you need to do the changes in onChange method. Use any unique property like name or index to identify which element has been changed, iterate the input array find that input element then update the value of that element. After updating the value in input array, update the state input array also, value will automatically reflect in UI when react re-render the component.

Check the working code:

class Module extends React.Component {
        constructor() {
            super()

            this.state = {
                inputs: [
                    { type: 'text', placeholder: 'placeholder text', name: 'text1', id: 'text1', value: 'aaa' },
                    { type: 'text', placeholder: 'another placeholder text', name: 'text2', id: 'text2', value: '' },
                    { type: 'text', placeholder: 'third placeholder text', name: 'text3', id: 'text3', value: '' },
                ]
            }

            this.handleInputChange = this.handleInputChange.bind(this)
        }

        handleInputChange(event) {
            let inputs = this.state.inputs.slice();
            for(let i in inputs){
                if(inputs[i].name == event.target.name){
                    inputs[i].value = event.target.value;
                    this.setState ({inputs});
                    break;
                }
            }
        }

        renderInput = (input, i) => {
            return(
                <div key={ input.id }>
                    <input
                        type={ input.type }
                        name={ input.name }
                        placeholder={ input.placeholder }
                        onBlur={ this.saveModule }
                        value={ input.value }
                        onChange={ this.handleInputChange }
                    />
                </div>
            )
        }

        render() {
            return (
                <div>
                    { this.state.inputs.map(this.renderInput) }
                </div>
            )
        }
    }


ReactDOM.render(<Module/>, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id='app'/>

Sign up to request clarification or add additional context in comments.

5 Comments

Can you explain what you've done? Replacing a bunch of code with more is not helpful to anyone who might be learning React
@DarrenSweeney updated the answer with proper information.
this works magic, thanks a lot and for the explanation, makes sense! :)
this.setState ({inputs}); at the end of the day, you are updating the states of ALL the inputs. Is that ok??
Surely there is a better way to do this than iterating over the whole array? can't you pass the element into the handle function?
0

You have to set the state by creating a new state first.

Since this.state is immutable, create a new state by only modifying the old state has to be done by using the spread operator or Object.assign.

This video is a good reference for how to change javascript's object in an immutable way. There is also a library made specifically for creating and modifying immutable objects efficiently called immutable.js.

Refer to this how to use immutable.js with react setState

(this code has not been tested)

class Module extends Component {
    constructor() {
        super()

        this.state = {
            inputs: [
                { type: 'text', placeholder: 'placeholder text', name: 'text1', id: 'text1', value: 'aaa' },
                { type: 'text', placeholder: 'another placeholder text', name: 'text2', id: 'text2', value: '' },
                { type: 'text', placeholder: 'third placeholder text', name: 'text3', id: 'text3', value: '' },
            ]
        }

        this.handleInputChange = this.handleInputChange.bind(this)
        this.saveModule = this.saveModule.bind(this)
    }

    // change this to an arrow function
    handleInputChange = (event) =>  {
        const name = event.target.name;
        const value = event.target.value;
        const oldInputState = this.state.inputs.find(i => i.name === name);
        // use the spread operator or Object.assign to create a brand new state object
        this.setState({
            ...,
            this.state, // copy everything from the old state
            // expect change the `inputs` value
            {inputs: [ // create a brand new array
                ...this.state.inputs.filter(i => i.name !== name),
                {...oldInputState, value} // replace old value with the new value
                // refer to that video I included, it's a great explanation
            ]}
        });
    }

    renderInput = (input) => {
        return(
            <div key={ input.id }>
                <input
                    type={ input.type }
                    name={ input.name }
                    placeholder={ input.placeholder }
                    onBlur={ this.saveModule }
                    value={ input.value }
                    onChange={ this.handleInputChange }
                />
            </div>
        )
    }

    render() {
        return (
            <div>
                { this.state.inputs.map(this.renderInput) }
            </div>
        )
    }
}

export default Module

Good luck!

1 Comment

i think, using arrow function with handleInputChange is not required, he already defined the binding in constructor.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.