简体   繁体   中英

React: Access properties of dynamically created elements by refs

I have a dynamically created list of 5 input elements. When I now click on a "plus" icon element (from IonicIcons) in React, I want the first of those input fields to be focused.

My input List:

if (actualState.showInputField === true) {
            inputField = (
            <div>       
                {                    
                    actualState.inputFields.map((val,index) => (   
                        <ListElemErrorBoundary key={index}>                   
                            <InputElement key={index} elemValue = {val} name={"input" +  index} onChangeListener={(event) => handleDoublettenIDs(event,index)} />
                        </ListElemErrorBoundary>  
                        )
                    )
                }                
                {actualState.errorMessage!= '' ? <Alert color="danger" >{actualState.errorMessage}</Alert> : null}
                {actualState.successMessage !='' ? <Alert color="success" >{actualState.successMessage}</Alert> : null} 
                <br />                    
                <p><button onClick = { () =>  {
                    handleInputs(props.anzeigeID);    
                    vanishMessage();                
                    }                
                }>absenden</button></p>   
            </div>  
            )
        }
        return inputField;
    }

My Icon:

const checkIcon = () => {
        let showIcon = null;
        if (actualState.showInputField === false) {
            showIcon = (
               <IoIosAddCircleOutline ref={toggleSignRef} onClick = {toggleInput}
                />
            )
        } else {
            showIcon = (
                <IoIosRemoveCircleOutline onClick = {toggleInput}
                />
            )
        }
        return showIcon;
    }

I probably should place my ref on the list items, however, I guess that for every new list element, this ref gets "overwritten" because I have only one ref. Should I do something like a input key query, to find out which list key input element this is, and if it is the first input key index, I execute a focus on that input element?

And how then can I retrieve the first input element inside the method toggleInput() where I set the showInputField value? Is it somehow possible to ask for the props.key of that input element's reference?

This component is a functional component and I use useRef only...

My Component:

import React, {useState, useRef, useEffect} from "react";
import { IoIosAddCircleOutline } from 'react-icons/io';
import { IoIosRemoveCircleOutline } from 'react-icons/io';
import InputElement from './inputElementDublette';
import fetch from 'isomorphic-unfetch';
import getConfig from 'next/config';
import ListElemErrorBoundary from './ListElemErrorBoundary';
import { Button, Alert  } from 'reactstrap';

let url_link;
let port = 7766; 

const { serverRuntimeConfig, publicRuntimeConfig } = getConfig();
const apiUrl = publicRuntimeConfig.apiUrl; //|| publicRuntimeConfig.apiUrl;
const server = publicRuntimeConfig.SERVERNAME;

let doublettenListe_link = `http://${server}:${port}/doubletten/`;


//functional component with state, with useState
const DubletteComponent = props => {
    const toggleSignRef = useRef();

    const [actualState, changeState] = useState({
        showInputField: false,
        dublettenIDs: [],
        errorMessage: '', 
        successMessage: '',  
        inputFields: ['','','','',''],                
        visible : false,
    });    


    const toggleInput = () => {
        changeState({...actualState, showInputField: !actualState.showInputField});
    }

    const vanishMessage = ()=>{    
          window.setTimeout(() => {
            changeState({
                ...actualState,
                errorMessage:'',
                successMessage: '',        
            });
          },7000);
      }


    const handleDoublettenIDs = (event,index) => { 
        let idnumber = event.target.value;
        let newInputFields = [...actualState.inputFields];
        newInputFields.splice(index,1, idnumber);
        //console.log("new:",newInputFields);
        if (isNaN(idnumber)) {
            changeState({...actualState, errorMessage: 'ID is not a number'})
        } if (idnumber > 2147483647) {
            changeState({...actualState, errorMessage: 'Number can not be bigger than 2147483647!'})
        }        
        else {
            changeState({...actualState, inputFields: newInputFields, errorMessage: '' });
        }      
    }

    const handleInputs = (anzeigeID) => {
        if (process.browser && apiUrl==='dev') {
            doublettenListe_link = `http://localhost:${port}/doubletten/`;
        }
        if (actualState.errorMessage=='') {
            let array = actualState.inputFields;
            let filtered = array.filter(function(el) {
                return el != '';
            });                                   
            const requestOptions = {
                method: 'POST',
                headers: {'Accept': 'application/json', 'Content-Type':'application/json'},            
                body: JSON.stringify({
                    id: anzeigeID,
                    dublettenIDs: filtered
                })
            };
                //console.log("inputfields:",filtered);
              // Note: I'm using arrow functions inside the `.fetch()` method.
              // This makes it so you don't have to bind component functions like `setState`
              // to the component.
              //console.log("link:", doublettenListe_link);
            fetch(doublettenListe_link , requestOptions)
            .then((response) => { 
                //console.log("Resp:", response);
                let tempArray = ['','','','',''];
                changeState({...actualState, inputFields: tempArray});   
                //console.log(actualState);        
                changeState({...actualState, dublettenIDs: []});  
                changeState({...actualState, successMessage: `Doubletten-IDs wurden erfolgreich eingetragen!`});

                return response;          
            }).catch((error) => {
                changeState({...actualState, errorMessage: `Error beim Eintrage der Dubletten. Bitte prüfen, ob der Server läuft. Error: ${error.statusText}`});
            });  
        }       
    }

    const checkIcon = () => {
        let showIcon = null;
        if (actualState.showInputField === false) {
            showIcon = (
               <IoIosAddCircleOutline onClick = {toggleInput}
                />
            )
        } else {
            showIcon = (
                <IoIosRemoveCircleOutline onClick = {toggleInput}
                />
            )
        }
        return showIcon;
    }

    const checkPrerequisites = () => {
        //let errorMessage = '';
        let inputField = null;
        // if (actualState.errorMessage != '') {
        //     errorMessage = (
        //         <Alert color="danger">{actualState.errorMessage}</Alert>
        //     )
        // }        

        //Outsourcing check for variable and return JSX elements on conditionals
        if (actualState.showInputField === true) {
            inputField = (
            <div>       
                {                    
                    actualState.inputFields.map((val,index) => (   
                        <ListElemErrorBoundary key={index}>                   
                            <InputElement key={index} elemValue = {val} name={"input" +  index} onChangeListener={(event) => handleDoublettenIDs(event,index)} />
                        </ListElemErrorBoundary>  
                        )
                    )
                }                
                {actualState.errorMessage!= '' ? <Alert color="danger" >{actualState.errorMessage}</Alert> : null}
                {actualState.successMessage !='' ? <Alert color="success" >{actualState.successMessage}</Alert> : null} 
                <br />                    
                <p><button onClick = { () =>  {
                    handleInputs(props.anzeigeID);    
                    vanishMessage();                
                    }                
                }>absenden</button></p>   
            </div>  
            )
        }
        return inputField;
    }

        return (            
            <div >
                {checkIcon()}  Dubletten eintragen   

                {checkPrerequisites()}                     


            </div>                               
        )      
    }

export default DubletteComponent

My InputElement Component:

const inputElement = (props) => (
    <p>
        <input 
            ref={props.ref}
            value ={props.elemValue}
            name={props.name}
            type="number" 
            max="2147483647"
            placeholder="Doubletten-ID" 
            onChange={props.onChangeListener}>            
        </input>
    </p>


)    

export default inputElement

The issue is you can not pass ref from your parent component to child component. In new version of react you can achieve this by using forwardRef api. Use it like below if you are in react @16 version.

import React from 'react'

const inputElement = React.forwardRef(props) => (
    <p>
        <input 
            ref={props.ref}
            value ={props.elemValue}
            name={props.name}
            type="number" 
            max="2147483647"
            placeholder="Doubletten-ID" 
            onChange={props.onChangeListener}>            
        </input>
    </p>
)  

//To focus textinput

this.inputref.focus();

Happy coding :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM