skip to Main Content

there are two components

  1. input – where the user will enter the value
  2. there is an output (H1) where this value will be displayed immediately

These two components are displayed in the parent file where they are as an array and are displayed in different places in html.
Also in the parent file, there is a button that creates 1 input and 1 output, which interact with each other and are independent of the rest of the same.

So far, the main problem is that two components display what I need when rendering, but do not change the value

Parrent

import InputTest from './InputTesting'
import TestOutput from './OutputTest'
const PageToCombine = () =>{
    const [title, setTitle] = useState('qwrqw!')
    const changeTitle = event => {
        setTitle(event.target.value);
    }

    const [inputItems, setInputItems] = useState([
        {id:32, type:"text", onchange:changeTitle, value:title},
    ])

    const [outputItems, setOutputItems] = useState([
        {id: 12, type:"text", value:title, onchange:changeTitle},    
    ])

   return(
        <div>
{inputItems.map((inputitem) =>
                <InputTest id={inputitem.id} type={inputitem.type} value={inputitem.value} onChange={inputitem.onchange}/>
            )}
            {outputItems.map(outputitem =>
                <TestOutput id={outputitem.id} type={outputitem.type} value={outputitem.value} onChange={outputitem.onchange}/>
            )}
</div>
    )
}


TestOutput component

const TestOutput = (props) =>{

    console.log(props)
    return(
        <div>
            <h1 {...props} onChange = {props.onChange}>{props.value}</h1>
        </div>
    )
    
}

InputTest component

const InputTest = (props) => {

    
    console.log(props)
    return(
        <input className={classes.inputTestStyle} {...props}/>
    )
}

3

Answers


  1. You’re on the right track to solving this problem, the key is to not pass a predefined onChange to the input but to generate one for each item. Thereby your parent is fully controlling your dynamic inputs.

    codesandbox demo

    export default function App() {
      const [inputs, setInputs] = useState([
        { id: 0, type: "text", value: "qwrqw!" }
      ]);
    
      return (
        <div>
          <button
            onClick={() => setInputs((p) => [...p, { id: p.length, value: "" }])}
          >
            New item
          </button>
          <div>
            {inputs.map((input) => (
              <Input
                key={input.id}
                onChange={(value) => {
                  setInputs((previous) =>
                    previous.map((p) =>
                      p.id === input.id ? { ...input, value } : p
                    )
                  );
                }}
                value={input.value}
              />
            ))}
          </div>
          <div>
            {inputs.map((input) => (
              <Output key={input.id} value={input.value} />
            ))}
          </div>
        </div>
      );
    }
    
    function Input({ onChange, value }) {
      return (
        <div>
          <input onChange={(e) => onChange(e.currentTarget.value)} value={value} />
        </div>
      );
    }
    
    function Output({ value }) {
      return <h1>{value}</h1>;
    }
    

    Notice that an id is used to identify each item, this is by React recommendations to not use indices as keys in case the items are reordered. That also means it’s up to you to generate a unique id.

    Login or Signup to reply.
  2. It looks like you try to use to many states you can just use one state for this

    const PageToCombine = () => {
      const [items, setItems] = useState([
        {
          id: 32,
          type: "text",
          value: "inital value",
        },
      ]);
      return (
        <div>
          {items.map((inputitem) => (
            <InputTest
              id={inputitem.id}
              type={inputitem.type}
              value={inputitem.value}
              onChange={(e) => {
                // set the value of the item with the same id as the input
                setItems((prevItems) => {
                  return prevItems.map((item) => {
                    if (item.id === inputitem.id) {
                      return { ...item, value: e.target.value };
                    }
                    return item;
                  });
                });
              }}
            />
          ))}
          {items.map((outputitem) => (
            <TestOutput
              id="{outputitem.id}"
              type="{outputitem.type}"
              value="{outputitem.value}"
            />
          ))}
        </div>
      );
    };
    
    Login or Signup to reply.
  3. You are using too many states and seems like you are not updating inputItems by setting setInputItems you should do like this with only one state for inputTest component and testOutput component

    Parent component

    import React, { useEffect, useState } from 'react'
    import InputTest from './InputTest';
    import TestOutput from './TestOutput ';
    export default function PageToCombine() {
    const [title, setTitle] = useState('qwrqw!')
    const [inputItems, setInputItems] = useState([
        { id: 32, type: "text", value: title },
        { id: 33, type: "text", value: title },
    ])
    const onchangeItems = (e, index) => {
        let _inputItems = [...inputItems];
        _inputItems[index].value = e.target.value
        setInputItems(_inputItems)
    }
    return (
        <div>
            {inputItems.map((inputitem, index) =>
                <>
                    <InputTest type={inputitem.type} value={inputitem.value} id=
                        {inputitem.id} onchange={(e) => onchangeItems(e, index)} />
                    <TestOutput type={inputitem.type} value={inputitem.value} id=
                        {inputitem.id} />
                </>
            )}
        </div>
    )
    

    }

    InputTest component

     export default function InputTest(props) {
       return (
        <input {...props} onChange={(e) => props.onchange(e,props.index)} />
       )
     }
    

    Outputtest component

    export default function TestOutput(props) {
       return (
         <div>
            <h1 {...props}>{props.value}</h1>
         </div>
       )
    }
     
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search