라우터

보통 일반적인 웹 애플리케이션에서는 URI로 요청을 보내면 서버에서 HTML 파일을 만들어서 내려주기 때문에 

페이지 새로고침이 일어나게된다. SPA는 클라이언트 측에서 화면을 렌더링하는 소스를 가지고 렌더링을 하기 때문에

새로고침이 일어나지 않으면서 화면의 변경이 이루어 진다. SPA 에서 URI에 따라 화면을 그려주는 것이다.

이 역할을 라우터가 한다.



React 는 라우터 기능을 지원하지 않기 때문에 라우팅을 하기 위한 라이브러리를 설치해주어야 한다.

npm install react-router-dom --save
npm install cross-env --save-dev

react-router-dom 은 라우팅을 할 수 있게 해주는 라이브러리 이고

cross-env는 프로젝트에서 NODE_PATH를 사용할수 있게 해주어

import 할 때 상대경로를 사용해야 하는데 설정에 따라 절대경로를 사용 할 수 있게 해주는 라이브러리 이다.



cross-env 설정

컴포넌트 import시에 절대경로를 사용할수있도록 package.json 파일의 scripts 를 수정한다

(../pages/page1 -> page/page1 이런식으로 import 할수 있게 해준다.)

"scripts": {
"start": "cross-env NODE_PATH=src react-scripts start",
"build": "cross-env NODE_PATH=src react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}


라우터 기본설정

src/client/App.js

컴포넌트를 특정 경로에 맵핑할 App.js 파일 생성

import React, { Component } from 'react';

class App extends Component {
render() {
return (
<div>
리액트 라우터
</div>
);
}
}

export default App;

src/client/Root.js

import React from 'react';
import { BrowserRouter } from 'react-router-dom';
import App from 'client/App';

const Root = () => (
<BrowserRouter>
<App/>
</BrowserRouter>
);

export default Root;

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import Root from 'client/Root';
import registerServiceWorker from './registerServiceWorker';

ReactDOM.render(<Root />, document.getElementById('root'));
registerServiceWorker();




pages 디렉토리 index.js 작성

src/pages/index.js

디렉토리의 index.js는 컴포넌트 import시에 각각 개별로 import 하지 않고 비구조화 할당으로 경로 한군데서 컴포넌트를 import 할 수 있게 해준다.

ex) import { Main, Page1, Pages, Basic } from 'pages';

export { default as Main } from './Main';
export { default as Pages } from './Pages';
export { default as Page1 } from './Page1';
export { default as Page2 } from './Page2';
export { default as Page3 } from './Page3';
export { default as Basic } from './Basic';




라우터 경로 맵핑

src/client/App.js

처음에 생성한 App.js에 컴포넌트와 경로들을 맵핑해준다.

import React, { Component } from 'react';
import { Route, Switch } from 'react-router-dom';
import Menu from '../components/Menu';
import { Main, Page1, Pages, Basic } from 'pages';


class App extends Component {
render() {
return (
<div>
<h2>React Tutorial</h2>
<Menu />
<Route exact path="/" component={Main}/>
<Switch>
<Route path="/page1/:name" component={Page1}/>
<Route path="/page1" component={Page1}/>
</Switch>
<Route path="/Pages" component={Pages}/>
<Route path="/basic" component={Basic}/>
</div>
);
}
}

export default App;


<Route> 컴포넌트에 path 는 경로 component는 맵핑할 컴포넌트를 지정해준다.


exact 속성은 경로가 정확히 일치해야만 라우터가 맵핑된 컴포넌트를 렌더링한다.


<Switch> 컴포넌트는 같은경로에 조건부로 라우팅을 하는 컴포넌트이다.

위 소스에서는 Switch컴포넌트에서 url경로로 파라미터가 전달된 경우와 전달되지 않은 경우를 구분하고 있다.

먼저 조건에 일치하는 컴포넌트만 렌더링 되기 때문에 조건이 들어가는 경로를 더 위에 작성해주어야 한다.





라우터 링크

src/components/Menu.js

SPA는 페이지가 새로고침되지 않으면서 경로를 이동해야 한다. a 태그를 사용해 링크를 걸면 페이지가 새로고침 되면서 페이지를 이동하기 때문에

Link 라는 컴포넌트를 사용해 경로를 링크한다.

import React from 'react';
import { Link } from 'react-router-dom';

const Menu = () => {
return (
<div>
<h3>메뉴</h3>
<ul>
<li><Link to="/">메인</Link></li>
<li><Link to="/Basic">기본 기능</Link></li>
<li><Link to="/Page1">라우터</Link></li>
<li><Link to="/Page1/BruceLee?name=BruceLee">라우터에 파라미터 전달</Link></li>
<li><Link to="/Pages">라우터 중첩</Link></li>
</ul>
<hr/>
</div>
);
};

export default Menu;




라우터에 파라미터 전달 / 값 사용, 쿼리스트링 사용

src/pages/page1.js

라우터 경로 맵핑시에 <Route path="/page1/:name" component={Page1}/> name이라는 변수명으로 파라미터를 전달받을수 있게 맵핑후

파라미터를 받아서 값을 사용 할 수 있다. 기존의 get 방식으로 쿼리스트링을 전달하는 방식으로 파라미터를 전달 할 수도 있다.


page1에 라우팅하면서 name 이라는 변수명 으로 'BruceLee' 전달, 쿼리스트링으로 name=BruceLee 전달하는 라우터 링크

<Link to="/Page1/BruceLee?name=BruceLee">라우터에 파라미터 전달</Link>


import React from 'react';
import queryString from 'query-string';

const Page1 = ({location, match}) => {
const query = queryString.parse(location.search);
const name = query.name

return (
<div>
<h2>
Page1
<br/>라우트에서 name이라는 명칭으로 지정한 params : {match.params.name}
<br/>url 쿼리로 전달받은 name : {name}
</h2>
</div>
);
};

export default Page1;

match.params 에는 <Route path="/page1/:name"> 로 전달한 값을 꺼내 사용 할 수있고


url에서 ?name=BruceLee 라고 전달한 

쿼리스트링의 경우에는 location.search 객체에서 꺼내 직접 파싱을 해도 되고 쿼리스트링 파싱 모듈을 사용해도 된다.

npm install query-string --save

쿼리스트링을 파싱하는 모듈을 설치하고 값을 파싱하는 코드

const query = queryString.parse(location.search);
const name = query.name



라우터 중첩 / NavLink 컴포넌트

src/pages/pages.js

라우터 컴포넌트 안에서 현재의 페이지를 유지하면서 페이지 내부에서 다른화면을 보여주는 라우터 중첩도 가능하다.

match.url 로 현재 라우터가 보여주고 있는 페이지를 유지하면서 그 하위 경로로 보여줄 컴포넌트들을 맵핑 시켜 라우터를 중첩한다.


NavLink 컴포넌트는 페이지가 라우팅되어 보여지고 있을 때 그 페이지를 링크하는 컴포넌트에 스타일을 줄 수 있는 컴포넌트이다.

activeStyle 이라는 속성으로 페이지가 보여지고 있을때 활성화시킬 style 을 주어 사용한다.


import React from 'react';
import { NavLink, Route } from 'react-router-dom';
import { Page1, Page2, Page3 } from 'pages';

const Pages = ({match}) => {
const activeStyle={color: 'red', fontSize: '16px'}
return (
<div>
<h2>Post List</h2>
<ul>
<li><NavLink to={`${match.url}/page1`} activeStyle={activeStyle}>페이지1</NavLink></li>
<li><NavLink to={`${match.url}/page2`} activeStyle={activeStyle}>페이지2</NavLink></li>
<li><NavLink to={`${match.url}/page3`} activeStyle={activeStyle}>페이지3</NavLink></li>
</ul>
<Route exact path={match.url} render={()=>(<h3>라우터 중첩</h3>)}/>
<Route path={`${match.url}/page1`} component={Page1}/>
<Route path={`${match.url}/page2`} component={Page2}/>
<Route path={`${match.url}/page3`} component={Page3}/>
</div>
);
};

export default Pages;









참고

https://velopert.com/reactjs-tutorials

리액트에서 state나 props가 변경되면 render 함수가 실행되고 Virtual DOM과 실제 DOM 조작이 일어나게 된다.

변경된 state나 props의 값이 이전과 동일하면 Virtual DOM 만 렌더링이 발생하고 실제 DOM은 렌더링 되지 않지만

데이터가 많은 경우에 DOM이 렌더링이 되지 않더라도 Virtual DOM은 변경이 일어나기 때문에 자원이 낭비가 된다.

이를 방지하려면 리액트의 라이프사이클인 shouldComponentUpdate 에서 변경이 일어날 state나 props를 

변경 이전의 state와 props를 체크하여 동일할 경우에는 렌더링을 하지않고 다른 경우에만 렌더링을 해야 한다.


 

데이터 필터링 + 컴포넌트 최적화 구현



키워드를 입력받아 Array 중 검색어와 일치하는 Object를 새로운 Array로 만들어 리스트 컴포넌트에 전달해주는 컴포넌트

class Optimize extends Component {
state = {
keyword: '',
userList: [
{
id: 0 ,
name: 'Bruce Lee'
},
{
id: 1 ,
name: 'Jackie Chan'
},
{
id: 2 ,
name: 'Chuck Norris'
},
{
id: 3 ,
name: 'Jet Li'
}
]
}
handleChange = (e) => {
this.setState({
keyword: e.target.value,
});
}
render() {
console.log('render optimize')
const { keyword, userList } = this.state
const filterItemList = userList.filter(
item => item.name.indexOf(keyword) !== -1
)
return (
<div>
<div><input type="text" value={keyword} onChange={this.handleChange}/></div>
<div>
<ItemList data={filterItemList}/>
</div>
</div>
);
}
}



Array를 받아서 하위 컴포넌트에 Array의 원소인 Object를  props로 전달하는 컴포넌트

이 컴포넌트에서 shouldComponentUpdate 라이프사이클에서 전달받은 Array인 이전 props와 변경이 일어날 props를

체크하고 있지만 상위 컴포넌트에서 매번 새로운 Array를 전달받기 때문에 무조건 true가 리턴되어 

계속 새로운 렌더링이 일어날수밖에없다.

class ItemList extends Component {
static defaultProps = {
data: []
}

shouldComponentUpdate(nextProps, nextState) {
return nextProps.data !== this.props.data
}

render() {
console.log('render ItemList')
const {data} = this.props
const itemList = data.map(item=>(<ItemTemplate key={item.id} item={item}/>))
return (
<div>
<div>
{itemList}
</div>
</div>
);
}
}


전달받은 Object의 내용을 보여주는 컴포넌트 shouldComponentUpdate 라이프사이클에서 전달받은 props를 이전props와 비교하여

동일할 경우엔 렌더링을 하지 않고 다른 Object인 경우만 렌더링 함

class ItemTemplate extends Component {
static defaultProps = {
item: { id: '' , name: ''}
}

shouldComponentUpdate(nextProps, nextState) {
return nextProps.item !== this.props.item;
}

render() {
console.log('render itemTemplate')
const {name} = this.props.item
return (
<div>
{name}
</div>
);
}
}







참고

https://reactjs.org/docs/getting-started.html

https://velopert.com/reactjs-tutorials

리액트에서 리스트를 추가하거나 변경하거나 제거할때는 해당 array를 직접 건드려서는 안된다.

array 를 직접 추가하는 push 나 제거하는 splice 등을 사용해서는 안되고

기존 array를 건들지 않고 기존의 array에 원하는 작업을 처리하고 가공하여 새로운 array로 리턴해주는 concat, map, filter 를 사용해야 한다.



Array에 새로운 Object 추가


값을 입력받고 등록 버튼을 누르면 부모 컴포넌트로 이벤트를 전달해주는 컴포넌트 작성

class AddForm extends Component {
static defaultProps = {
onCreate: () => { console.warn('onCreate')}
}
state = {
name:''
}
handleChange = (e)=>{
this.setState({
name: e.target.value
})
}
handleClick = ()=>{
this.props.onCreate({name:this.state.name})
this.setState({
name: ''
})
}
render() {
const name = this.state.name
return (
<div>
<input type='text' value={name} onChange={this.handleChange}/>
<button onClick={this.handleClick}>등록</button>
</div>
);
}
}


Array의 Object를 수정, 삭제


Array의 내용을 보여주고 수정 버튼이나 삭제버튼 클릭시 수정 폼으로 전환하여 수정할수 있는 상태로 바꿔주고 삭제하며

이벤트를 부모 컴포넌트에게 전달하는 컴포넌트 작성

class ItemTemplate extends Component {
static defaultProps = {
item: { id: '' , name: ''},
onRemove: () => { console.warn('onRemove')},
onUpdate: () => { console.warn('onUpdate')},
}
state = {
isEdit: false,
name: ''
}
handleChangeMode = ()=> {
this.setState({
isEdit: (this.state.isEdit) ? false : true
})
}
handleChange = (e)=>{
this.setState({
name: e.target.value
})
}
handleRemove = () => {
const { item, onRemove } = this.props;
onRemove(item.id)
}
componentDidUpdate(prevProps, prevState) {
// 수정버튼 or 확인버튼을 클릭하면 state.sEdit 이 토글되고 render() 가 재 호출되기 전
// 일반상태- > 수정상태의 경우 props의 name을 state의 name에 넣어준다
// 수정상태 -> 일반상태의 경우 state의 name을 props의 이벤트에 파라미터로 넘긴다.

const { item, onUpdate } = this.props;
// 일반상태 -> 수정상태 : isEdit true -> false
if(!prevState.isEdit && this.state.isEdit) {
this.setState({
name: item.name,
})
}
// 수정상태 -> 일반상태 : isEdit false -> true
if (prevState.isEdit && !this.state.isEdit) {
onUpdate(item.id, {
name: this.state.name,
});
}
}
render() {
const {isEdit} = this.state;

// 수정상태
if(isEdit){
const {name} = this.state;
return(
<div>
<input type="text" value={name} onChange={this.handleChange}/>
<button onClick={this.handleChangeMode}>확인</button>
</div>
)
}
// 일반상태
else{
const {name} = this.props.item;
return(
<div>
{name}
<button onClick={this.handleChangeMode}>수정</button>
<button onClick={this.handleRemove}>삭제</button>
</div>
)
}
}
}



Array의 변경사항을 반영하는 컴포넌트

class ListRender extends Component {
state = {
userList:[
{
id: 0 ,
name: 'Bruce Lee'
},
{
id:1 ,
name: 'React'
},
{
id:2 ,
name: 'user'
}
]
}
// Array에 신규 Object 추가
handleCreate = (data) => {
// 파라미터로 받은 데이터를 Array에 추가
const { userList } = this.state;
this.setState({
// push가 아닌 concat 함수를 사용해야함
userList: userList.concat({ id: userList[userList.length-1].id+1, ...data })
})
}
// 삭제 처리
handleRemove = (id) => {
// 파라미터로 받은 id와 Array의 Object의 id와 일치하면
// 해당 id와 일치하는 object를 제거하고 state에 반영
const { userList } = this.state;
this.setState({
// filter 함수는 Array의 원소중에 조건에 일치하는 원소만으로 새로운 Array를 만들어 리턴해줌
userList: userList.filter(user => user.id !== id)
})
}
// 수정 처리
handleUpdate = (id, data) => {
// 파라미터로 받은 id와 Array의 Object의 id와 일치하면
// 해당 id와 일치하는 object를 수정하고 state에 반영
const { userList } = this.state;
this.setState({
// map 함수는 forEach처럼 Array의 길이만큼 callback을 실행하여 처리된 Array를 리턴해줌
// 파라미터로 받은 id와 일치하는 Object를 찾아 기존의 데이터에 새로운 데이터를 덮어쓴다.
userList: userList.map(
user => id === user.id
? { ...user, ...data }
: user
)
})
}
render() {
const {userList} = this.state
const itemList = userList.map(item=>
(<ItemTemplate key={item.id} item={item}
onUpdate={this.handleUpdate}
onRemove={this.handleRemove}/>)
)
return (
<div>
<div><AddForm onCreate={this.handleCreate}/></div>
<div>{itemList}</div>
</div>
);
}
}




결과





참고


https://reactjs.org/docs/getting-started.html

https://velopert.com/reactjs-tutorials

리액트에서 폼의 데이터를 바인딩 하는 방법은 value와 값이 변경됐을때의 이벤트를 부모컴포넌트에서 props로 전달해주고

자식컴포넌트에서 값의 변경이 일어나면 부모컴포넌트에서 받은 이벤트를 실행하는 방법을 사용해야 한다.

리액트는 데이터가 부모에서 자식으로 단방향으로 흐르기 떄문에 양방향바인딩을 구현하려면 이런 방법을 사용해야 한다.



text 바인딩


class InputTextBind extends Component {
state = {
name: '',
age:''
}
handleChange = (e) => {
this.setState({
[e.target.name]: e.target.value
})
}
render() {
return (
<form>
<input
value={this.state.name}
onChange={this.handleChange}
name="name"
/>
<div>{this.state.name}</div>
<input
value={this.state.age}
onChange={this.handleChange}
name="age"
/>
<div>{this.state.age}</div>
</form>
);
}
}


value를 컴포넌트 내의 state의 값과 바인딩하고 change 이벤트 발생시에 값을 setState를 해주는 형태이다.

input이 여러개인 경우 input 태그에 name 속성을 넣어 handleChange 함수에서 파라미터로 받은 event에 들어있는

e.target에 name으로 구분을 하여 값을 업데이트 한다.



결과






Select 바인딩

select 바인딩의 경우는 value를 state.selectedValue와 바인딩하고 

option을 state.selectList 를 리스트 렌더링 형식으로 렌더링 한다.


class SelectBind extends Component {
state= {
selectedValue: '',
defaultValue: '선택하세요',
selectList:[
{text:'A', value: '1'},
{text:'B', value: '2'},
{text:'C', value: '3'}
]
}
handleChange = (e)=>{
this.setState({
selectedValue: e.target.value
})
}
render() {
const selectList = this.state.selectList
const list = selectList.map(option=>(<option key={option.value} value={option.value}>{option.text}</option>))
return (
<form>
<select value={this.state.selectedValue} onChange={this.handleChange}>
<option>{this.state.defaultValue}</option>
{list}
</select>
</form>
);
}
}





참고

https://reactjs.org/docs/getting-started.html

https://velopert.com/reactjs-tutorials


리액트에서 리스트를 렌더링 하려면 Array의 원소를 컴포넌트에서 props 로 받아 원소의 내용을 보여줄 컴포넌트를 만들고

그 컴포넌트를 Array의 길이만큼 생성해주는 컴포넌트를 만들어야 한다.



먼저 Array의 원소의 내용을 보여줄 컴포넌트를 작성

부모 컴포넌트에게서 props로 item 이라는 object를 받아서 <div>에 id와 name 을 보여주는 컴포넌트 이다.


class ItemTemplate extends Component {
static defaultProps = {
item: { id: '' , name: ''}
}
render() {
const {id, name} = this.props.item;
return (
<div>
<div>{id}, {name}</div>
</div>
);
}
}



그리고 이 컴포넌트를 Array의 길이만큼 생성해줄 컴포넌트 작성


class ListRender extends Component {
state = {
userList:[
{
id: 0 ,
name: 'Bruce Lee'
},
{
id:1 ,
name: 'React'
},
{
id:2 ,
name: 'user'
}
]
}
render() {
const {userList} = this.state
const itemList = userList.map(item=>(<ItemTemplate key={item.id} item={item}/>))
return (
<div>
<div>
{itemList}
</div>
</div>
);
}
}


state 에 userList 라는 Array가 있고 그 Array를 컴포넌트 내부에서 itemList 라는 변수에다 담는다.

이 itemList를 map이라는 함수를 사용해 JSX 문법으로 풀어줘야 한다.

Array.map 함수는 forEach 비슷한건데 Array의 길이만큼 callback 함수를 실행시키고 새로운 array를 만들어 리턴해주는 함수이다.

map 함수의 callback 내에서 처음에 작성한 ItemTemplate 라는 컴포넌트에 props를 전달하는 형태로 만들어주고

ListRender 컴포넌트에서는 그 리스트를 렌더링한다.

리스트를 렌더링 할 때는 key 라는 props에다 원소의 고유한 값(id)을 전달해 주어야 한다.




결과






참고


https://reactjs.org/docs/getting-started.html

https://velopert.com/reactjs-tutorials

'React.js' 카테고리의 다른 글

리액트 리스트 CRUD  (0) 2018.07.12
리액트 폼 바인딩 React Form Binding  (0) 2018.07.05
리액트 라이프사이클 React Life Cycle  (0) 2018.07.02
리액트 state React state  (0) 2018.06.30
리액트 props React props  (0) 2018.06.30

리액트는 컴포넌트 생성 전,후 데이터 업데이트 전,후 컴포넌트 제거 등이 이루어질때마다 

호출되는 라이프사이클 훅이 있다.




일반적으로 사용되는 라이프 사이클 훅


constructor

컴포넌트 생성 전에 호출되며 컴포넌트의 state를 할당하거나 이벤트를 컴포넌트에 바인딩할 때 사용한다.

constructor 는 먼저 코드를 작성하기 전에 super(props); 를 작성하여 상위클래스의 생성자를 호출 해주어야 한다.

constructor(props) {
super(props);
// Don't call this.setState() here!
this.state = { counter: 0 };
this.handleClick = this.handleClick.bind(this);
}


componentDidMount

컴포넌트를 렌더링 한 직후에 호출되며 이 단계에서 컴포넌트에서 보여줄 데이터들을 가져오는것이 좋다.

componentDidMount() {
console.log('componentDidMount')
}


componentDidUpdate

컴포넌트에서 setState로 컴포넌트의 state가 변경되면 render() 가 다시 호출되어 컴포넌트가 재 렌더링 될때 호출된다.

파라미터인 prevProps, prevState, snapshot 으로 변경되기 이전의 값 들을 조회 해서 비교하는 작업 등을 할 수 있다.

componentDidUpdate(prevProps, prevState, snapshot) {
console.log('componentDidUpdate')
}


componentWillUnmount

컴포넌트가 제거 될 때 호출된다.

setInteval 같은 컴포넌트가 사라지면 유지되어선 안되는 작업들을 이 단계에서 제거 해 주어야 한다.

componentWillUnmount(){
console.log('componentWillUnmount')
}


 



일반적으로 사용되지 않는 라이프 사이클 훅



shouldComponentUpdate

이 라이프사이클 훅은 state가 변경되어 컴포넌트가 재 렌더링 되기 전에 호출되며

새로운 state가 이전 state와 값이 다른지 비교하여 재 렌더링을 할것인지 말것인지를 결정하는데 사용한다.

성능을 최적화 하는 목적의 용도이며 

true를 반환하면 재 렌더링을하고 false를 반환하면 재 렌더링을 하지 않고 이전의 상태를 유지한다.

shouldComponentUpdate(nextProps, nextState) {
console.log('shouldComponentUpdate')
return true
}

getSnapshotBeforeUpdate

컴포넌트가 재 렌더링 할 때 실제로 DOM 을 변경하는 과정이 일어나기 전에 호출되는 라이프 사이클 훅이다.

마지막으로 변경이 일어난 렌더링 시점의 DOM의 정보(width, height 같은)를 스냅샷으로 반환해 줄 수 있다.

이 반환된 스냅샷은 componentDidUpdate 훅에서 매개변수로 전달되어 사용 할 수 있다.

class ScrollingList extends React.Component {
constructor(props) {
super(props);
this.listRef = React.createRef();
}

getSnapshotBeforeUpdate(prevProps, prevState) {
// Are we adding new items to the list?
// Capture the scroll position so we can adjust scroll later.
if (prevProps.list.length < this.props.list.length) {
const list = this.listRef.current;
return list.scrollHeight - list.scrollTop;
}
return null;
}

componentDidUpdate(prevProps, prevState, snapshot) {
// If we have a snapshot value, we've just added new items.
// Adjust scroll so these new items don't push the old ones out of view.
// (snapshot here is the value returned from getSnapshotBeforeUpdate)
if (snapshot !== null) {
const list = this.listRef.current;
list.scrollTop = list.scrollHeight - snapshot;
}
}

render() {
return (
<div ref={this.listRef}>{/* ...contents... */}</div>
);
}
}


componentDidCatch

이 라이프 사이클 훅은 컴포넌트에서 에러 발생시 호출되는 훅이고 

에러가 발생했을때 조건부 렌더링으로 에러상황을 대처 할 때 사용한다.

class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}

componentDidCatch(error, info) {
// Display fallback UI
this.setState({ hasError: true });
// You can also log the error to an error reporting service
logErrorToMyService(error, info);
}

render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}







참고


https://reactjs.org/docs/getting-started.html

https://velopert.com/reactjs-tutorials

'React.js' 카테고리의 다른 글

리액트 폼 바인딩 React Form Binding  (0) 2018.07.05
리액트 리스트 렌더링 React List Rendering  (0) 2018.07.03
리액트 state React state  (0) 2018.06.30
리액트 props React props  (0) 2018.06.30
React 조건부 렌더링  (0) 2018.06.29

리액트는 컴포넌트 내부에서 사용하는 데이터가 props 와 state 가 있다.

props는 부모 컴포넌트에서 전달받은 데이터고 변경이 불가능하다.

state는 컴포넌트 자신의 데이터이며 변경이 가능하다.


import React, { Component } from 'react';

class State extends React.Component {

constructor(props) {
super(props);
this.state = {
number: 0
};

this.increment = this.increment.bind(this)
}

increment (){
this.setState({
number: this.state.number + 1
})
}

decrement = ()=>{
this.setState({
number: this.state.number - 1
})
}

render(){
return (
<div>
<h2>{this.state.number}</h2>
<div><button onClick={this.increment}>증가</button></div>
<div><button onClick={this.decrement}>감소</button></div>
</div>
);
}
}
export default State ;



state 안의 number 라는 값이 증가 감소 버튼을 클릭하면 바뀌는 소스이다.


state는 constructor 에서 선언을 하고 값을 변경할때 

state.number = 값 이런식으로 변경을 하게되면 리액트가 랜더링을 하지 않는다.

setState 라는 함수를 써서 변경을 해야지만 리액트가 render() 함수를 재호출하고 변경된 state가 화면에 나타나게 된다.


increment (){
this.setState({
number: this.state.number + 1
})
}


위의 increment 함수는 에러가 발생하는함수이다.

increment 함수만으로는 리액트 컴포넌트의 this를 찾을수가 없어서 인데 이 함수를 동작하게 하려면


constructor(props) {
super(props);
this.state = {
number: 0
};

this.increment = this.increment.bind(this)
}


constructor 에서 this.increment = this.increment.bind(this) 로 this 를 바인딩 해주어야 한다.


decrement = ()=>{
this.setState({
number: this.state.number - 1
})
}


decrement 함수의 경우는 ()=> arrow 펑션을 사용해서 함수를 작성했는데

매번 constructor 에서 this 를 바인딩 하기 귀찮다면 이런식으로 함수를 작성해야 한다.

이런식으로 작성하는것을 클래스 필드 구문 이라고 한다.



그리고 state를 constructor 에서 선언하지 않고 밖에서 할수 있기도 하다.


import React, { Component } from 'react';

class State extends React.Component {

constructor(props) {
super(props);
this.increment = this.increment.bind(this)
}

state = {
number: 0
}

increment (){
this.setState({
number: this.state.number + 1
})
}

decrement = ()=>{
this.setState({
number: this.state.number - 1
})
}

render(){
return (
<div>
<h2>{this.state.number}</h2>
<div><button onClick={this.increment}>증가</button></div>
<div><button onClick={this.decrement}>감소</button></div>
</div>
);
}
}
export default State ;


state를 이런식으로 작성하는게 좀 더 보기 편해보이는 것 같다.



결과





참고


https://reactjs.org/docs/getting-started.html

https://velopert.com/reactjs-tutorials




props 는 부모 컴포넌트에서 자식컴포넌트에게 전달해주어 받은 값이고

읽기 전용이므로 수정 할 수 없다.

값을 사용하려면 수정하지 않고 받은 그대로의 값만 사용 가능하다.




먼저 부모컴포넌트에서 받은 props 를 보여주는 컴포넌트를 하나 만든다.


import React, { Component } from 'react';

class Props extends Component {
render() {
let style = {
textAlign: 'center'
}
return (
<div>
<div style={style}>
<span>부모 컴포넌트에서 받은 name: {this.props.name}</span>
</div>
</div>
);
}
}

export default Props;


컴포넌트에 style 을 주려고하다가 style을 일반 html에서 주듯이 하면 안된다는 것을 알았다.

style도 props로 취급되어 style="text-align: center;" 이런식으로 주면 안되고

변수를 하나 선언하여 그안에 style 속성을 담고 컴포넌트 내부에서 중괄호를 열어 변수를 전달해주는 식으로 스타일을 주어야한다.

또 text-align 이나 background-color 처럼 - 가 들어가서는 안되고 -가 들어가는 다음 문자를 camel 표현식처럼 대문자로 작성해주어야 한다.





자식 컴포넌트에 props 를 전달하며 랜더링하는 컴포넌트를 작성한다.


import React, { Component } from 'react';
import Props from './components/Props'



class App extends Component {
render() {
return (
<div className="App">
<Props name="React"/>
</div>
);
}
}


export default App;



결과





참고


https://reactjs.org/docs/getting-started.html

https://velopert.com/reactjs-tutorials


'React.js' 카테고리의 다른 글

리액트 라이프사이클 React Life Cycle  (0) 2018.07.02
리액트 state React state  (0) 2018.06.30
React 조건부 렌더링  (0) 2018.06.29
React 컴포넌트 내부에서 변수, 함수 사용  (0) 2018.06.28
React 컴포넌트  (0) 2018.06.28

+ Recent posts