programing

React 요소에 대한 고유한 키를 만드는 방법

sourcejob 2023. 3. 20. 23:09
반응형

React 요소에 대한 고유한 키를 만드는 방법

목록을 만들어 저장할 수 있는 리액트 앱을 만들고 있는데, 리액트는 내 요소에 고유한 키 프로펠(Elements List/List Form)이 없다고 경고해왔습니다.사용자가 작성한 요소에 대해 고유한 키 프로포트를 작성하려면 어떻게 해야 합니까?다음은 리액트 코드입니다.

var TitleForm = React.createClass({
    handleSubmit: function(e) {
        e.preventDefault();
        var listName = {'name':this.refs.listName.value};
        this.props.handleCreate(listName);
        this.refs.listName.value = "";
    },
    render: function() {
        return (
            <div>
                <form onSubmit={this.handleSubmit}>
                    <input className='form-control list-input' type='text' ref='listName' placeholder="List Name"/>
                    <br/>
                    <button className="btn btn-primary" type="submit">Create</button>
                </form>
            </div>
        );
    }
});

var ListForm = React.createClass({
    getInitialState: function() {
        return {items:[{'name':'item1'}],itemCount:1};
    },
    handleSubmit: function(e) {
        e.preventDefault();
        var list = {'name': this.props.name, 'data':[]};
        var items = this.state.items;
        for (var i = 1; i < items.length; i++) {
            list.data.push(this.refs[items[i].name]);
        }
        this.props.update(list);
        $('#'+this.props.name).remove();
    }, 
    handleClick: function() {
        this.setState({
            items: this.state.items.concat({'name':'item'+this.state.itemCount+1}),
            itemCount: this.state.itemCount+1
        });
    },
    handleDelete: function() {
        this.setState({
            itemCount: this.state.itemCount-1
        });
    },
    render: function() {
        var listItems = this.state.items.map(function(item) {
            return (
                <div>
                    <input type="text" className="list-form" placeholder="List Item" ref={item.name}/>
                    <br/>
                </div>
            );
        });
        return (
            <div>
                <form onSubmit={this.handleSubmit} className="well list-form-container">
                    {listItems}
                    <br/>
                    <div onClick={this.handleClick} className="btn btn-primary list-button">Add</div>
                    <div onClick={this.handleDelete} className="btn btn-primary list-button">Delete</div>
                    <button type="submit" className="btn btn-primary list-button">Save</button>
                </form>
            </div>
        )
    }
});


var List = React.createClass({
    getInitialState: function() {
        return {lists:[], savedLists: []};
    },
    handleCreate: function(listName) {
        this.setState({
            lists: this.state.lists.concat(listName)
        });
    },
    updateSaved: function(list) {
        this.setState({
            savedLists: this.state.savedLists.concat(list)
        });
    },
    render: function() {
        var lst = this;
        var lists = this.state.lists.map(function(list) {
            return(
                <div>
                    <div key={list.name} id={list.name}>
                        <h2 key={"header"+list.name}>{list.name}</h2>
                        <ListForm update={lst.updateSaved} name={list.name}/>
                    </div>
                </div>
            )
        });
        var savedLists = this.state.savedLists.map(function(list) {
            var list_data = list.data;
            list_data.map(function(data) {
                return (
                    <li>{data}</li>
                )
            });
            return(
                <div>
                    <h2>{list.name}</h2>
                    <ul>
                        {list_data}
                    </ul>
                </div>
            )
        });
        var save_msg;
        if(savedLists.length == 0){
            save_msg = 'No Saved Lists';
        }else{
            save_msg = 'Saved Lists';
        }
        return (
            <div>
                <TitleForm handleCreate={this.handleCreate} />
                {lists}
                <h2>{save_msg}</h2>
                {savedLists}
            </div>
        )
    }
});

ReactDOM.render(<List/>,document.getElementById('app'));

내 HTML:

<div class="container">
    <h1>Title</h1>
    <div id="app" class="center"></div>
</div>

으로 만들 수 .unique keys가장 간단한 방법은 배열을 반복할 때 인덱스를 사용하는 것입니다.

    var lists = this.state.lists.map(function(list, index) {
        return(
            <div key={index}>
                <div key={list.name} id={list.name}>
                    <h2 key={"header"+list.name}>{list.name}</h2>
                    <ListForm update={lst.updateSaved} name={list.name}/>
                </div>
            </div>
        )
    });

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★」this.state.lists.map콜백에도 두 번째 파라미터를 전달할 수 있습니다.이 파라미터는 콜백의index값은 어레이 내의 모든 항목에 대해 고유합니다.

그리고 이렇게 쓸 수 있어요.

<div key={index}>

여기에서도 같은 작업을 할 수 있습니다.

    var savedLists = this.state.savedLists.map(function(list, index) {
        var list_data = list.data;
        list_data.map(function(data, index) {
            return (
                <li key={index}>{data}</li>
            )
        });
        return(
            <div key={index}>
                <h2>{list.name}</h2>
                <ul>
                    {list_data}
                </ul>
            </div>
        )
    });

편집

그러나, 유저 Martin Dawson이 이하의 코멘트에서 지적한 것처럼, 이것이 항상 이상적인 것은 아닙니다.

그럼 해결책은 뭘까요?

많이

  • 고유한 키/ID/숫자/문자열을 생성하는 함수를 생성하여 사용할 수 있습니다.
  • uuid, uniqid 의 기존 npm 패키지를 사용할 수 있습니다.
  • '아무렇게나 하다'와 같은 도 있습니다.new Date().getTime();에 붙여서 합니다.
  • 마지막으로 데이터베이스에서 얻은 고유 ID를 사용하는 것이 좋습니다.

예:

const generateKey = (pre) => {
    return `${ pre }_${ new Date().getTime() }`;
}

const savedLists = this.state.savedLists.map( list => {
    const list_data = list.data.map( data => <li key={ generateKey(data) }>{ data }</li> );
    return(
        <div key={ generateKey(list.name) }>
            <h2>{ list.name }</h2>
            <ul>
                { list_data }
            </ul>
        </div>
    )
});

React는 RESTABLE 키를 예상합니다. 즉, 사용자가 키를 한 번 할당하고 목록의 모든 항목이 매번 동일한 키를 수신해야 합니다. 이렇게 하면 React가 가상 DOM을 조정할 때 데이터 변경에 대해 최적화하고 재렌더해야 하는 구성 요소를 결정할 수 있습니다.따라서 UUID를 사용하는 경우 UI 수준이 아닌 데이터 수준에서 수행해야 합니다.

키에 할 수 여러 로 결합할 수 . , ID는 여러 개입니다.${username}_${timestamp}예를 들어 채팅 회선에 대한 훌륭한 고유 키가 될 수 있습니다.

키는 React가 변경/추가/삭제된 항목을 식별하는 데 도움이 되며 요소를 안정적으로 식별하기 위해 어레이 내의 요소에 제공해야 합니다.

이를 염두에 두고 아래에 설명된 바와 같이 기본적으로 세 가지 다른 전략이 있습니다.

  1. 정적 요소(html 상태(포커스, 커서 위치 등)를 유지할 필요가 없는 경우)
  2. 편집 가능하고 정렬 가능한 요소
  3. 편집 가능하지만 정렬할 수 없는 요소

React Documentation의 설명에 따르면 NAT은 요소에 안정적인 ID를 부여해야 하며, 따라서 고객의 요구에 가장 적합한 전략을 신중하게 선택해야 합니다.

정적 요소

리액트 매뉴얼에서도 알 수 있듯이 항목 순서가 변경될 수 있는 경우 키에 인덱스를 사용하지 않는 것이 좋습니다.로 인해 퍼포먼스에 악영향을 미쳐 컴포넌트 상태에 문제가 발생할 수 있습니다."

테이블, 리스트 등 정적 요소의 경우 shortid라는 도구를 사용하는 것이 좋습니다.

1) NPM/YARN을 사용하여 패키지를 설치합니다.

npm install shortid --save

2) 사용하고 싶은 클래스 파일을 Import 합니다.

import shortid from 'shortid';

2) 새로운 ID를 생성하는 명령어는 shortid.generate()입니다.

3) 예:

  renderDropdownItems = (): React.ReactNode => {
    const { data, isDisabled } = this.props;
    const { selectedValue } = this.state;
    const dropdownItems: Array<React.ReactNode> = [];

    if (data) {
      data.forEach(item => {
        dropdownItems.push(
          <option value={item.value} key={shortid.generate()}>
            {item.text}
          </option>
        );
      });
    }

    return (
      <select
        value={selectedValue}
        onChange={this.onSelectedItemChanged}
        disabled={isDisabled}
      >
        {dropdownItems}
      </select>
    );
  };

중요: Respect Virtual DOM은 키가 필요합니다.요소가 재렌더될 때마다 shortid를 사용하여 새로운 키가 생성되고 포커스나 커서 위치 등의 html 상태가 해제됩니다.위의 전략은 목록이나 읽기 전용 필드처럼 값이 변경되지 않는 요소를 구축하는 경우에만 유용하므로 키를 생성하는 방법을 결정할 때 이 점을 고려하십시오.

편집 가능(정렬 가능) 필드

요소가 정렬 가능하고 항목의 고유 ID가 있는 경우 추가 문자열과 결합합니다(한 페이지에 동일한 정보를 두 번 포함해야 하는 경우).이것이 가장 권장되는 시나리오입니다.

:

  renderDropdownItems = (): React.ReactNode => {
    const elementKey:string = 'ddownitem_'; 
    const { data, isDisabled } = this.props;
    const { selectedValue } = this.state;
    const dropdownItems: Array<React.ReactNode> = [];

    if (data) {
      data.forEach(item => {
        dropdownItems.push(
          <option value={item.value} key={${elementKey}${item.id}}>
            {item.text}
          </option>
        );
      });
    }

    return (
      <select
        value={selectedValue}
        onChange={this.onSelectedItemChanged}
        disabled={isDisabled}
      >
        {dropdownItems}
      </select>
    );
  };

편집 가능(정렬 불가) 필드(예: INPUT ELEMENTS)

마지막 수단으로 입력과 같이 편집 가능하지만 정렬할 수 없는 필드의 경우 요소 키는 복제할 수 없으므로 일부 시작 텍스트와 함께 일부 인덱스를 사용할 수 있습니다.

:

  renderDropdownItems = (): React.ReactNode => {
    const elementKey:string = 'ddownitem_'; 
    const { data, isDisabled } = this.props;
    const { selectedValue } = this.state;
    const dropdownItems: Array<React.ReactNode> = [];

    if (data) {
      data.forEach((item:any index:number) => {
        dropdownItems.push(
          <option value={item.value} key={${elementKey}${index}}>
            {item.text}
          </option>
        );
      });
    }

    return (
      <select
        value={selectedValue}
        onChange={this.onSelectedItemChanged}
        disabled={isDisabled}
      >
        {dropdownItems}
      </select>
    );
  };

이게 도움이 됐으면 좋겠다.

사용 안 함return `${ pre }_${ new Date().getTime()}`;이상적인 것은 아니지만 목록 컴포넌트 간에 일관성이 확보되고 새로운 날짜 기능을 사용하면 일관성이 유지되기 때문에 어레이 인덱스를 사용하는 것이 좋습니다., 마다 진정한 의 새로운 .

고유 키는 글로벌하게 고유해야 하는 것이 아니라 컴포넌트 컨텍스트에서 고유해야 하므로 항상 불필요한 재렌더가 실행되지 않습니다.처음에는 새로운 Date와 관련된 문제를 느끼지 못할 것입니다만, 예를 들어 이미 렌더링된 목록으로 돌아가야 할 경우, React는 어떤 컴포넌트가 변경되었는지, 어떤 컴포넌트가 변경되지 않았는지 모르기 때문에 모두 혼란스러워지기 시작하고 메모리 누수가 발생합니다.Date 키에 따라 메모리 누수가 발생할 수 있습니다.모든 컴포넌트가 변경되었습니다.

이제 내 대답이야.YouTube 동영상 목록을 렌더링한다고 가정해 보겠습니다.비디오 ID(arqTu9Ay4Ig)를 일의의 ID로서 사용합니다.이렇게 하면 해당 ID가 변경되지 않으면 구성 요소는 그대로 유지되지만, 변경되면 React는 새 비디오임을 인식하고 그에 따라 변경합니다.

그렇게 엄격할 필요는 없습니다.좀 더 여유로운 변형은 Erez Hochman이 이미 지적한 것처럼 제목을 사용하거나 컴포넌트의 속성(타이틀+카테고리)을 조합하여 사용하는 것입니다.그러면 React에게 변경 여부를 확인하도록 지시할 수 있습니다.

중요하지 않은 것을 편집했다

리액트에게 키 할당

React를 활용할 수 있습니다.하위 API:

const { Children } = React;

const DATA = [
  'foo',
  'bar',
  'baz',
];

const MyComponent = () => (
  <div>
    {Children.toArray(DATA.map(data => <p>{data}</p>))}
  </div>
);


ReactDOM.render(<MyComponent />,document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

2021년의 최신 솔루션을 추가하려면...

프로젝트 나노이드는 빠르고 매우 작으면서도 키로 사용할 수 있는 독특한 스트링 ID를 제공한다는 것을 알게 되었습니다.

을 사용하여 설치 후npm install nanoid, 다음과 같이 사용합니다.

import { nanoid } from 'nanoid';

// Have the id associated with the data.
const todos = [{id: nanoid(), text: 'first todo'}];

// Then later, it can be rendered using a stable id as the key.
const todoItems = todos.map((todo) =>
  <li key={todo.id}>
    {todo.text}
  </li>
)

다른 옵션은 weak-key입니다.https://www.npmjs.com/package/weak-key

import weakKey from "weak-key";

const obj1 = {a : 42};
const obj2 = {b : 123};
const obj3 = {a : 42};

console.log(weakKey(obj1)); // 'weak-key-1'
console.log(weakKey(obj2)); // 'weak-key-2'
console.log(weakKey(obj3)); // 'weak-key-3'

console.log(weakKey(obj1)); // 'weak-key-1'

텍스트 문자열의 단순한 배열을 위해 다음 두 가지 방법 중 하나를 시도합니다.

1. encodeURI이는 NodeJS와 브라우저 모두에서 사용 가능합니다.


const WithEncoder = () => {
  const getKey = useCallback((str, idx) => encodeURI(`${str},${idx}`), [])

  return (
    <div>
      {["foo", "bar"].map((str, idx) => (
        <div key={getKey(str, idx)}>{str}</div>
      ))}
    </div>
  )
}

2. window.btoa브라우저에서만 사용할 수 있습니다.

const WithB2A = () => {
  const getKey = useCallback((str, idx) => window.btoa(`${str}-${idx}`), [])

  return (
    <div>
      {["foo", "bar"].map((str, idx) => (
        <div key={getKey(str, idx)}>{str}</div>
      ))}
    </div>
  )
}

상황에 따라서는, 「uniqueId creator」를 선택해 주세요.단, 드래그&드롭등의 아이템을 렌더링 할 때, 각 아이템에 uniqueId가 없는 경우는, 그 데이터를 redux, mapper, 어디에 있는, 그 데이터에 uniqueId를 재매핑해, 각 아이템에 고유의 Id를 추가하는 것을 추천합니다(<Item key={...}와 같은 렌더링에는 추가하지 않습니다).}) 왜냐하면 React는 렌더링 간에 어떤 확인도 수행할 수 없었기 때문입니다(그리고 그 모든 이점).

컴포넌트에서 새 ID를 사용할 수 있습니다.

다음은 순서 변경, 추가, 편집 및 삭제 작업입니다.일단 설정된 키는 변경되지 않으므로 불필요한 재렌더가 필요하지 않습니다.일부에서는 show stopper가 될 수 있는 한 가지 문제: 처음에 "_reactKey"라고 렌더링할 때 오브젝트에 속성을 추가해야 합니다.

psuedo TS의 기능 컴포넌트의 예(스니펫으로 실행되지 않음):

interface IRow{

  myData: string,
  _reactKey?:number
  }


export default function List(props: {
    rows: Array<IRow>
}) {
    const {myRows} = props;
    const [nextKey, setNextKey] = useState(100);
    const [rows, setRows] = useState<Array<IRow>|undefined>();


    useEffect(function () {
        if (myRows) {
            for (let row of myRows){
                if (!row._reactKey){
                    row._reactKey = nextKey;
                    setNextKey(nextKey+1);
                }
            }
            setRows(myRows);
        } else if (!rows) {
            setRows([]);
        }
    }, [myRows, columns]);
    
    addRow(){
    
        let newRow = { blah, blah, _reactKey : nextKey};
        setNextKey(nextKey+1);
        rows.push(newRow);
        setRows({...rows});
    
    }
    
    function MyRow(props:{row:IRow}){
        const {row} = props;
        
        return <tr><td>{row._reactKey}</td><td>row.myData</td></tr>
    
    }
    
    return <table>
        <tr><th>Index</th><th>React Key</th><th>My Data</th></tr>
        rows.map((row, key)=>{
            return <MyRow key={row._reactKey} row={row} />
        }
        </table>
        
}
            
    
    
    

리액션을 많이 사용하지 않지만, 마지막으로 이 문제를 발견했을 때 새로운 상태 어레이를 만들고 키를 추적했습니다.

const [keys, setKeys] = useState([0]);
const [items, setItems] = useState([value: "", key: 0,])

그런 다음 목록에 새 항목을 추가할 때 키 배열에서 마지막 키를 가져와 1을 추가한 다음 setKeys를 사용하여 키 배열을 업데이트합니다.다음과 같은 경우:

const addItemWithKey = () => {
  // create a new array from the state variable
  let newKeyArr = [...keys];
  // create a new array from the state variable that needs to be tracked with keys
  let newItemArr = [...items];
  // get the last key value and add 1
  let key = newKeyArr[newKeyArr.length-1] + 1;
  newKeyArr.push(key);
  newItemArr.push({value: "", key: key,});
  // set the state variable 
  setKeys(newKeyArr);
  setItems(newItemArr);
};

Keys Array는 컴포넌트의 반복에만 사용되고 있기 때문에 값을 삭제할 염려가 없습니다.또, 리스트로부터 항목을 삭제하거나 새로운 항목을 추가하는 경우는, 해결하려고 하고 있습니다.키 배열에서 마지막 번호를 가져와 하나를 추가하면 항상 고유한 키를 가질 수 있습니다.

import React, {useState} from 'react';
import {SafeAreaView,ScrollView,StyleSheet,Text,View,Dimensions} from 'react-native';
const {width}=Dimensions.get('window');
function sayfalar(){
  let pages=[]
  for (let i = 0; i < 100; i++) {    
    pages.push(<View key={i} style={styles.pages}><Text>{i}</Text></View>)
  }
   return pages
}
const App=()=>{
  return(
    
    <View style={styles.container}>
    <ScrollView horizontal={true} pagingEnabled={true}>
    {sayfalar()}
    
    </ScrollView>
    </View>
  )
}
const styles = StyleSheet.create({
  container:{
    flexDirection:'row',
    flex:1
  },
  pages:{
    width:width
  }
})
export default App;

매핑된 인덱스 사용(i)

things.map((x,i) => {
<div key=i></div>
});

이게 도움이 됐으면 좋겠다.

react-module-id를 사용하면 uniq id를 쉽게 생성할 수 있습니다.https://www.npmjs.com/package/react-html-id

2021년에 가장 빠른 해결책은 uniqid를 사용하는 것입니다.자세한 내용은 https://www.npmjs.com/package/uniqid를 참조해 주세요.요약:

  • 단말기와 프로젝트 파일 첫 번째: npm install uniqid
  • 프로젝트에서 uniqid 가져오기
  • 필요한 모든 키로 사용할 수 있습니다.
 uniqid = require('uniqid');
                return(
                    <div>
                        <div key={ uniqid() } id={list.name}>
                            <h2 key={ uniqid() }>{list.name}</h2>
                            <ListForm update={lst.updateSaved} name={list.name}/>
                        </div>
                    </div>
                )
            });

사용하고 있습니다.

<div key={+new Date() + Math.random()}>

언급URL : https://stackoverflow.com/questions/39549424/how-to-create-unique-keys-for-react-elements

반응형