[React] 중요 내용 정리

19 minute read

자식 컴포넌트가 부모한테 값 전달하기

배열 데이터 삽입하기

자식 컴포넌트가 부모한테 값 전달하기 위해서는 App 컴포넌트 내부에서 handleCreate라는 메소드를 만든다. 만든 메소드를 자식컴포넌트에게 <PhoneForm onCreate={this.handleCreate} /> props로 전달해주고, props로 전달한 함수 onCreate={...} 를 호출시켜서 data 값이 App.js 로 들어간다.

 1// App.js
 2import React, { Component } from "react";
 3import "./App.css";
 4import PhoneForm from "./components/PhoneForm";
 5
 6class App extends Component {
 7  id = 0; // id는 렌더링 되는 값이 아니기 때문에 state 값 안에 넣어줄 필요 없음
 8
 9  state = {
10    information: []
11  };
12  handleCreate = data => {
13    const { information } = this.state; // 비구조 할당
14
15    // 불변성 유지. 기존것 기반으로 값 주입. concat
16    this.setState({
17      information: information.concat({
18        ...data,
19        id: this.id++
20
21        /* 또는 다른 방법1: 
22        name: data.name,
23        phone: data.phone,
24        id: this.id++
25
26      방법2:
27        .concat(Object.assign( {}, data, {
28          id: this.id++
29        }))
30        
31      */
32      })
33      // 비구조할당 안쓰면 this.state.information.concat(data)가 된다.
34    });
35  };
36
37  render() {
38    return (
39      <div>
40        <PhoneForm onCreate={this.handleCreate} />
41        {JSON.stringify(this.state.information)}
42      </div>
43    );
44  }
45}
46
47export default App;
 1// PhoneForm.js
 2import React, { Component } from "react";
 3
 4class PhoneForm extends Component {
 5  state = {
 6    name: "",
 7    phone: ""
 8  };
 9
10  handleChange = e => {
11    this.setState({
12      [e.target.name]: e.target.value
13    });
14  };
15
16  handleSumbit = e => {
17    e.preventDefault();
18    this.props.onCreate({
19      name: this.state.name,
20      phone: this.state.phone
21    });
22    this.setState({
23      name: "",
24      phone: ""
25    });
26  };
27
28  render() {
29    return (
30      <form onSubmit={this.handleSumbit}>
31        <input
32          name="name"
33          placeholder="name"
34          onChange={this.handleChange}
35          value={this.state.name}
36        />
37        <input
38          name="phone"
39          placeholder="phone number"
40          onChange={this.handleChange}
41          value={this.state.phone}
42        />
43        <button type="submit">submit</button>
44      </form>
45    );
46  }
47}
48
49export default PhoneForm;

베열 렌더링하기

JavaScript 배열 내장함수 map()

map()

1const numbers = [1, 2, 3, 4, 5];
2const squared = numbers.map(n => n * n);
3console.log(squared); // [1, 4, 9, 16, 25]

PhoneInfoList 에 적용

 1// App.js
 2import React, { Component } from "react";
 3import "./App.css";
 4import PhoneForm from "./components/PhoneForm";
 5import PhoneInfo from "./components/PhoneInfo";
 6import PhoneInfoList from "./components/PhoneInfoList";
 7
 8class App extends Component {
 9  id = 0;
10
11  state = {
12    information: []
13  };
14  handleCreate = data => {
15    const { information } = this.state; // 비구조 할당
16
17    // 불변성 유지. 기존것 기반으로 값 주입. concat
18    this.setState({
19      information: information.concat(
20        Object.assign({}, data, {
21          id: this.id++
22        })
23      )
24    });
25  };
26
27  render() {
28    return (
29      <div>
30        <PhoneForm onCreate={this.handleCreate} />
31        <PhoneInfoList data={this.state.information} />
32      </div>
33    );
34  }
35}
36
37export default App;
 1// PhoneForm.js
 2import React, { Component } from "react";
 3
 4class PhoneForm extends Component {
 5  state = {
 6    name: "",
 7    phone: ""
 8  };
 9
10  handleChange = e => {
11    this.setState({
12      [e.target.name]: e.target.value
13    });
14  };
15
16  handleSumbit = e => {
17    e.preventDefault();
18    this.props.onCreate({
19      name: this.state.name,
20      phone: this.state.phone
21    });
22    this.setState({
23      name: "",
24      phone: ""
25    });
26  };
27
28  render() {
29    return (
30      <form onSubmit={this.handleSumbit}>
31        <input
32          name="name"
33          placeholder="name"
34          onChange={this.handleChange}
35          value={this.state.name}
36        />
37        <input
38          name="phone"
39          placeholder="phone number"
40          onChange={this.handleChange}
41          value={this.state.phone}
42        />
43        <button type="submit">submit</button>
44      </form>
45    );
46  }
47}
48
49export default PhoneForm;
 1// PhoneInfo.js
 2import React, { Component } from "react";
 3
 4class PhoneInfo extends Component {
 5  render() {
 6    const { name, phone, id } = this.props.info;
 7
 8    const style = {
 9      border: "1px solid black",
10      padding: "8px",
11      margin: "8px"
12    };
13
14    return (
15      <div style={style}>
16        <div>
17          <b>{name}</b>
18        </div>
19        <div>{phone}</div>
20      </div>
21    );
22  }
23}
24
25export default PhoneInfo;
 1// PhoneInfoList.js 
 2import React, { Component } from "react";
 3import PhoneInfo from "./PhoneInfo";
 4
 5class PhoneInfoList extends Component {
 6  static defaultProps = {
 7    data: []
 8  };
 9  render() {
10    const { data } = this.props;
11
12    const list = data.map(info => <PhoneInfo info={info} key={info.id} />);
13    return <div>{list}</div>;
14  }
15}
16
17export default PhoneInfoList;

배열 렌더링, key

key가 없다면?

key가 없으면 순차적으로 모든 값이 바뀌게 된다. key가 있으면 해당 값만 사이에 끼어들거나 삭제되게 된다.

배열 데이터 삭제하기

불변성 유지하면서 작업 처리 필요. .slice, .filter 사용.

.slice

기존의 배열 건드리지 않음( 불변성 지킴 )

 1const numbers = [1, 2, 3, 4, 5];
 2numbers.slice(0, 2);
 3// [1, 2]     기존의 numbers 배열은 건드리지 않음
 4
 5numbers.slice(0, 2).concat(numbers.slice(3, 5))
 6// [1, 2, 4, 5]   3을 제외한 값 출력 가능
 7
 8[
 9    ...numbers.slice(0, 2),
10    10,
11    ...numbers.slice(3, 5)
12]
13// [1, 2, 10, 4, 5]

.filter

기존의 배열 건드리지 않음( 불변성 지킴 )

1const numbers = [1, 2, 3, 4, 5];
2numbers.filter(n => n > 3);
3// [4, 5]
4
5numbers.filter(n => n !== 3);
6// [1, 2, 4, 5]

적용 : 데이터 삭제

 1// App.js
 2import React, { Component } from "react";
 3import "./App.css";
 4import PhoneForm from "./components/PhoneForm";
 5import PhoneInfo from "./components/PhoneInfo";
 6import PhoneInfoList from "./components/PhoneInfoList";
 7
 8class App extends Component {
 9  id = 0;
10
11  state = {
12    information: []
13  };
14  handleCreate = data => {
15    const { information } = this.state; // 비구조 할당
16
17    // 불변성 유지. 기존것 기반으로 값 주입. concat
18    this.setState({
19      information: information.concat(
20        Object.assign({}, data, {
21          id: this.id++
22        })
23      )
24    });
25  };
26
27  handleRemove = (id) => {
28    const { information } = this.state;
29    this.setState({
30      information: information.filter(info => info.id !== id)
31    });
32  }
33
34  render() {
35    return (
36      <div>
37        <PhoneForm onCreate={this.handleCreate} />
38        <PhoneInfoList 
39          data={this.state.information} 
40          onRemove={this.handleRemove}
41        />
42
43      </div>
44    );
45  }
46}
47
48export default App;
 1// PhoneForm.js
 2import React, { Component } from "react";
 3
 4class PhoneForm extends Component {
 5  state = {
 6    name: "",
 7    phone: ""
 8  };
 9
10  handleChange = e => {
11    this.setState({
12      [e.target.name]: e.target.value
13    });
14  };
15
16  handleSumbit = e => {
17    e.preventDefault();
18    this.props.onCreate({
19      name: this.state.name,
20      phone: this.state.phone
21    });
22    this.setState({
23      name: "",
24      phone: ""
25    });
26  };
27
28  render() {
29    return (
30      <form onSubmit={this.handleSumbit}>
31        <input
32          name="name"
33          placeholder="name"
34          onChange={this.handleChange}
35          value={this.state.name}
36        />
37        <input
38          name="phone"
39          placeholder="phone number"
40          onChange={this.handleChange}
41          value={this.state.phone}
42        />
43        <button type="submit">submit</button>
44      </form>
45    );
46  }
47}
48
49export default PhoneForm;
 1// PhoneInfo.js
 2import React, { Component } from "react";
 3
 4class PhoneInfo extends Component {
 5
 6    handleRemove = () => {
 7        const { info, onRemove } = this.props;
 8        onRemove(info.id);
 9    }
10  render() {
11    const { name, phone } = this.props.info;
12
13    const style = {
14      border: "1px solid black",
15      padding: "8px",
16      margin: "8px"
17    };
18
19    return (
20      <div style={style}>
21        <div><b>{name}</b></div>
22        <div>{phone}</div>
23        <button onClick={this.handleRemove}>삭제</button>
24      </div>
25    );
26  }
27}
28
29export default PhoneInfo;
 1// PhoneInfoList.js
 2import React, { Component } from "react";
 3import PhoneInfo from "./PhoneInfo";
 4
 5class PhoneInfoList extends Component {
 6  static defaultProps = {
 7    data: []
 8  };
 9  render() {
10    const { data, onRemove } = this.props;
11
12    const list = data.map(info => (
13      <PhoneInfo 
14        onRemove={onRemove} 
15        info={info} 
16        key={info.id} 
17      />
18    ));
19    return <div>{list}</div>;
20  }
21}
22
23export default PhoneInfoList;

배열 안의 데이터 수정하기

배열 안의 데이터 수정할 때는 .slice , .map 을 사용할 수 있다.

.slice

1const numbers = [1, 2, 3, 4, 5];
2[
3    ...numbers.slice(0, 2),
4    9,
5    ...numbers.slice(3, 5)
6]

.map

1const numbers = [1, 2, 3, 4, 5];
2numbers.map(n => {
3    if(n === 3) {
4        return 9;
5    } return n;
6});

적용

 1// App.js
 2import React, { Component } from "react";
 3import "./App.css";
 4import PhoneForm from "./components/PhoneForm";
 5import PhoneInfo from "./components/PhoneInfo";
 6import PhoneInfoList from "./components/PhoneInfoList";
 7
 8class App extends Component {
 9  id = 0;
10
11  state = {
12    information: []
13  };
14  handleCreate = data => {
15    const { information } = this.state; // 비구조 할당
16
17    // 불변성 유지. 기존것 기반으로 값 주입. concat
18    this.setState({
19      information: information.concat(
20        Object.assign({}, data, {
21          id: this.id++
22        })
23      )
24    });
25  };
26
27  handleRemove = (id) => {
28    const { information } = this.state;
29    this.setState({
30      information: information.filter(info => info.id !== id)
31    });
32  }
33
34  handleUpdate = (id, data) => {
35    const { information } = this.state; // 비구조화 할당 통해서 information에 대한 레퍼런스 가져옴
36    this.setState({
37      information: information.map(
38        info => {
39          if(info.id === id) {
40            return {
41              id,
42              ...data,
43            };
44          }
45          return info;
46        }
47      )
48    })
49  }
50
51  render() {
52    return (
53      <div>
54        <PhoneForm onCreate={this.handleCreate} />
55        <PhoneInfoList 
56          data={this.state.information} 
57          onRemove={this.handleRemove}
58          onUpdate={this.handleUpdate}
59        />
60
61      </div>
62    );
63  }
64}
65
66export default App;
 1// PhoneForm.js
 2import React, { Component } from "react";
 3
 4class PhoneForm extends Component {
 5  state = {
 6    name: "",
 7    phone: ""
 8  };
 9
10  handleChange = e => {
11    this.setState({
12      [e.target.name]: e.target.value
13    });
14  };
15
16  handleSumbit = e => {
17    e.preventDefault();
18    this.props.onCreate({
19      name: this.state.name,
20      phone: this.state.phone
21    });
22    this.setState({
23      name: "",
24      phone: ""
25    });
26  };
27
28  render() {
29    return (
30      <form onSubmit={this.handleSumbit}>
31        <input
32          name="name"
33          placeholder="name"
34          onChange={this.handleChange}
35          value={this.state.name}
36        />
37        <input
38          name="phone"
39          placeholder="phone number"
40          onChange={this.handleChange}
41          value={this.state.phone}
42        />
43        <button type="submit">submit</button>
44      </form>
45    );
46  }
47}
48
49export default PhoneForm;
 1// PhoneInfo.js
 2import React, { Component, Fragment } from "react";
 3
 4class PhoneInfo extends Component {
 5
 6    state = {
 7        editing: false,
 8        name: '',
 9        phone: '',
10    }
11
12    handleRemove = () => {
13        const { info, onRemove } = this.props;
14        onRemove(info.id);
15    }
16
17    handleToggleEdit = () => { // editing 값을 반전시킴. f -> t, t -> f
18        // true -> false
19          // onUpdate
20        // false -> true
21          // state에 info 값들 넣어주기
22        const { info, onUpdate } = this.props;
23        if (this.state.editing) {
24            onUpdate(info.id, {
25                name: this.state.name,
26                phone: this.state.phone
27            });
28        } else {
29            this.setState({
30                name: info.name,
31                phone: info.phone,
32            });
33        }
34
35        this.setState({
36            editing: !this.state.editing,
37        })
38    }
39
40    handleChange = (e) => {
41        this.setState({
42            [e.target.name]: e.target.value
43        })
44    }
45
46  render() {
47    const { name, phone } = this.props.info;
48    const { editing } = this.state;
49
50    const style = {
51      border: "1px solid black",
52      padding: "8px",
53      margin: "8px"
54    };
55
56    return (
57      <div style={style}>
58        {
59            editing ? (
60                <Fragment>
61                  <div>
62                      <input
63                        name="name"
64                        onChange={this.handleChange} 
65                        value={this.state.name}
66                      />
67                  </div>
68                  <div>
69                      <input
70                        name="phone"
71                        onChange={this.handleChange} 
72                        value={this.state.phone}
73                      />
74                  </div>
75                </Fragment>
76            ) : (
77                <Fragment>
78                  <div><b>{name}</b></div>
79                  <div>{phone}</div>
80                </Fragment>
81            )
82        }
83        <button onClick={this.handleRemove}>삭제</button>
84        <button onClick={this.handleToggleEdit}>
85          { editing ? '적용' : '수정' }
86        </button>
87      </div>
88
89    );
90  }
91}
92
93export default PhoneInfo;
 1// PhoneInfoList.js
 2import React, { Component } from "react";
 3import PhoneInfo from "./PhoneInfo";
 4
 5class PhoneInfoList extends Component {
 6  static defaultProps = {
 7    data: []
 8  };
 9  render() {
10    const { data, onRemove, onUpdate } = this.props;
11
12    const list = data.map(info => (
13      <PhoneInfo 
14        onRemove={onRemove} 
15        onUpdate={onUpdate}
16        info={info} 
17        key={info.id} 
18      />
19    ));
20    return <div>{list}</div>;
21  }
22}
23
24export default PhoneInfoList;

최적화, 활용, Ref

shouldComponentUpdate를 통한 최적화. 불변성을 왜 유지하는가

불변성을 왜 유지하는가?

불필요한 렌더링을 막아서 최적화 시킬 수 있다. shouldComponentUpdate 를 통해서 업데이트가 불필요할 때는 업데이트를 하지 않도록 해줘야 한다. 이 경우 PhoneInfo.jsshouldComponentUpdate 를 구현한다.

컴포넌트 업데이트 성능 최적화

현재의 props와 nextProps의 값을 비교해서 다를 때 업데이트 하게 만든다.

 1// PhoneInfo.js
 2(...)
 3 
 4shouldComponentUpdate(nextProps, nextState) {
 5    if (this.state !== nextState) {   // 현재의 props와 nextProps의 값을 비교
 6        return true;
 7    }
 8    return this.props.info !== nextProps.info; // 동일하면 렌더함수 다시 호출 안함
 9}
10
11(...)

이름으로 전화번호 찾기

 1// App.js
 2state = {
 3  (...)
 4  keyword: '',      // keyword 생성
 5};
 6
 7handleChange = (e) => {     // hadleChange 함수 구현
 8  this.setState({
 9    keyword: e.target.value,
10  })
11}
12(...)
13render() {
14  return (
15    <div>
16      (...)
17      <input        // Serach 만들고 연결
18        value={this.state.keyword}
19        onChange={this.handleChange}
20        placeholder="Search"
21      />
22      <PhoneInfoList   
23        data={this.state.information.filter(  // filter 구현
24          info => info.name.indexOf(this.state.keyword) > -1
25        )} 
26        (...)
27      />
28
29    </div>
30  );
31}

Ref를 통하여 DOM에 직접 접근하기

입력후 focus를 name에 있게 변경하기. ref를 통해서 DOM에 직접 접근해야 한다.

아래 코드에 직접 접근해야 한다. 이때 ref를 사용.

1// PhoneForm.js
2 (...)
3<input
4  name="name"
5  placeholder="name"
6  onChange={this.handleChange}
7  value={this.state.name}
8/>
9 (...)

ref 사용 방법 두가지

1. 함수 사용

ref={ function } 라는 값에 함수를 작성한다. 이 함수는 refparameter 로 받아서 이 컴포넌트의 멤버 변수로 ref 값을 넣어주는 작업을 한다.

 1// PhoneForm.js
 2(...)
 3 
 4class PhoneForm extends Component {
 5  input = null;
 6 
 7 (...)
 8
 9  render() {
10    return (
11      <form onSubmit={this.handleSumbit}>
12        <input
13          name="name"
14          placeholder="name"
15          onChange={this.handleChange}
16          value={this.state.name}
17          ref={ref => this.input = ref}   // ref 함수
18  (...)

this.input 은 상단의 input 인데 rendering 되면 input = ref 에 의해서 값이 ref 가 된다.

1// PhoneForm.js
2  handleSubmit = e => {
3    (...)
4    this.input.focus();
5  };

HandleSubmit 이 발생했을 때 input DOM에 직접적으로 접근 해서 focus() 를 찾는다.

2. React.createRef() 사용

 1// PhoneForm.js
 2(...)
 3 
 4class PhoneForm extends Component {
 5  input = React.createRef();   // React.createRef();
 6
 7 (...)
 8  
 9    handleSubmit = e => {
10     (...)
11    this.input.current.focus();  // current.focus()
12    };
13
14 (...)
15
16  render() {
17    return (
18        (...)
19          ref={this.input}   // ref 함수
20  (...)

React.createRef() 를 이용하면 current 를 통해서 해당 DOM에 접근 가능하다. 그러므로 this.input.current.focus() 로 작성한다.

전체코드 정리

 1// App.js
 2import React, { Component } from "react";
 3import "./App.css";
 4import PhoneForm from "./components/PhoneForm";
 5import PhoneInfo from "./components/PhoneInfo";
 6import PhoneInfoList from "./components/PhoneInfoList";
 7
 8class App extends Component {
 9  id = 3;
10
11  state = {
12    information: [
13      {
14        id: 0,
15        name: 'Gouda',
16        phone: '010-0000-0001'
17      },
18      {
19        id: 1,
20        name: '자두',
21        phone: '010-0000-0002'
22      },
23      {
24        id: 2,
25        name: '돌돌이',
26        phone: '010-0000-0003'
27      }
28    ],
29    keyword: '',
30  };
31
32  handleChange = (e) => {
33    this.setState({
34      keyword: e.target.value,
35    })
36  }
37
38  handleCreate = data => {
39    const { information } = this.state; // 비구조 할당
40
41    // 불변성 유지. 기존것 기반으로 값 주입. concat
42    this.setState({
43      information: information.concat(
44        Object.assign({}, data, {
45          id: this.id++
46        })
47      )
48    });
49  };
50
51  handleRemove = (id) => {
52    const { information } = this.state;
53    this.setState({
54      information: information.filter(info => info.id !== id)
55    });
56  }
57
58  handleUpdate = (id, data) => {
59    const { information } = this.state; // 비구조화 할당 통해서 information에 대한 레퍼런스 가져옴
60    this.setState({
61      information: information.map(
62        info => {
63          if(info.id === id) {
64            return {
65              id,
66              ...data,
67            };
68          }
69          return info;
70        }
71      )
72    })
73  }
74
75  render() {
76    return (
77      <div>
78        <PhoneForm onCreate={this.handleCreate} />
79        <input 
80          value={this.state.keyword}
81          onChange={this.handleChange}
82          placeholder="Search"
83        />
84        <PhoneInfoList 
85          data={this.state.information.filter(
86            info => info.name.indexOf(this.state.keyword) > -1
87          )} 
88          onRemove={this.handleRemove}
89          onUpdate={this.handleUpdate}
90        />
91
92      </div>
93    );
94  }
95}
96
97export default App;
 1// PhoneForm.js
 2import React, { Component } from "react";
 3
 4class PhoneForm extends Component {
 5  input = React.createRef();
 6  state = {
 7    name: "",
 8    phone: ""
 9  };
10
11  handleChange = e => {
12    this.setState({
13      [e.target.name]: e.target.value
14    });
15  };
16
17  handleSubmit = e => {
18    e.preventDefault();
19    this.props.onCreate({
20      name: this.state.name,
21      phone: this.state.phone
22    });
23    this.setState({
24      name: "",
25      phone: ""
26    });
27    this.input.current.focus();
28  };
29
30  render() {
31    return (
32      <form onSubmit={this.handleSubmit}>
33        <input
34          name="name"
35          placeholder="name"
36          onChange={this.handleChange}
37          value={this.state.name}
38          ref={this.input}
39        />
40        <input
41          name="phone"
42          placeholder="phone number"
43          onChange={this.handleChange}
44          value={this.state.phone}
45        />
46        <button type="submit">submit</button>
47      </form>
48    );
49  }
50}
51
52export default PhoneForm;
  1// PhoneInfo.js
  2import React, { Component, Fragment } from "react";
  3
  4class PhoneInfo extends Component {
  5
  6    state = {
  7        editing: false,
  8        name: '',
  9        phone: '',
 10    }
 11
 12    shouldComponentUpdate(nextProps, nextState) {
 13        if (this.state !== nextState) {
 14            return true;
 15        }
 16        return this.props.info !== nextProps.info;   
 17        // 동일하면 렌더함수 다시 호출 안함
 18    }
 19
 20    handleRemove = () => {
 21        const { info, onRemove } = this.props;
 22        onRemove(info.id);
 23    }
 24
 25    handleToggleEdit = () => { // editing 값을 반전시킴. f -> t, t -> f
 26        // true -> false
 27          // onUpdate
 28        // false -> true
 29          // state에 info 값들 넣어주기
 30        const { info, onUpdate } = this.props;
 31        if (this.state.editing) {
 32            onUpdate(info.id, {
 33                name: this.state.name,
 34                phone: this.state.phone
 35            });
 36        } else {
 37            this.setState({
 38                name: info.name,
 39                phone: info.phone,
 40            });
 41        }
 42
 43        this.setState({
 44            editing: !this.state.editing,
 45        })
 46    }
 47
 48    handleChange = (e) => {
 49        this.setState({
 50            [e.target.name]: e.target.value
 51        })
 52    }
 53
 54  render() {
 55    const { name, phone } = this.props.info;
 56    const { editing } = this.state;
 57
 58    const style = {
 59      border: "1px solid black",
 60      padding: "8px",
 61      margin: "8px"
 62    };
 63
 64    console.log(name);
 65
 66    return (
 67      <div style={style}>
 68        {
 69            editing ? (
 70                <Fragment>
 71                  <div>
 72                      <input
 73                        name="name"
 74                        onChange={this.handleChange} 
 75                        value={this.state.name}
 76                      />
 77                  </div>
 78                  <div>
 79                      <input
 80                        name="phone"
 81                        onChange={this.handleChange} 
 82                        value={this.state.phone}
 83                      />
 84                  </div>
 85                </Fragment>
 86            ) : (
 87                <Fragment>
 88                  <div><b>{name}</b></div>
 89                  <div>{phone}</div>
 90                </Fragment>
 91            )
 92        }
 93        <button onClick={this.handleRemove}>삭제</button>
 94        <button onClick={this.handleToggleEdit}>
 95          { editing ? '적용' : '수정' }
 96        </button>
 97      </div>
 98
 99    );
100  }
101}
102
103export default PhoneInfo;
 1// PhoneInfoList.js
 2import React, { Component } from "react";
 3import PhoneInfo from "./PhoneInfo";
 4
 5class PhoneInfoList extends Component {
 6  static defaultProps = {
 7    data: []
 8  };
 9  render() {
10    const { data, onRemove, onUpdate } = this.props;
11
12    console.log('rendering list');
13
14    const list = data.map(info => (
15      <PhoneInfo 
16        onRemove={onRemove} 
17        onUpdate={onUpdate}
18        info={info} 
19        key={info.id} 
20      />
21    ));
22    return <div>{list}</div>;
23  }
24}
25
26export default PhoneInfoList;