주의사항 (오류)
npm install -g cnpm --registry=https://registry.npm.taobao.org
'react-scripts'은(는) 내부 또는 외부 명령, 실행할 수 있는 프로그램, 또는 배치 파일이 아닙니다.
// 다른 컴포넌트를 설치한 후 npm start 명령을 다시 실행할 때 위 오류가 발생할 수 있습니다.
## 해결 방법 1:
npm install react-scripts
설치 완료 후 npm start를 다시 실행합니다.
## 해결 방법 2:
node_modules 폴더 삭제
npm install
설치 완료 후 npm start를 다시 실행합니다.
설치 명령어 약어
==> i = install (설치)
> -g = global (전역)
> -S = --save (개발 환경에 배포)
> -D = --save-dev (프로덕션 환경에 배포)
세 가지 라이브러리 파일
react.min.js - React의 핵심 라이브러리
react-dom.min.js - DOM 관련 기능 제공
babel.min.js - Babel은 ES6 코드를 ES5 코드로 변환하여, 아직 ES6를 지원하지 않는 브라우저에서도 React 코드를 실행할 수 있게 합니다. Babel은 JSX를 지원합니다. Babel과 babel-sublime 패키지를 함께 사용하면 소스 코드의 구문 강조를 한 차원 높일 수 있습니다.
공개 저장소 설치 (Redux)
npm i redux -S
store 생성 -> index.js, reducer.js, actionCreators.js, actionTypes.js
// index.js (디버깅 도구 > REDUX_DEVTOOLS 설치)
import { createStore } from 'redux';
import reducer from './reducer';
const store = createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
export default store;
// actionTypes.js
export const CHANGE_INPUT_VALUE = 'CHANGE_INPUT_VALUE';
// actionCreators.js
import { CHANGE_INPUT_VALUE } from './actionTypes';
export const getInputChangeAction = (value) => ({
type: CHANGE_INPUT_VALUE,
value
});
// reducer.js
import { CHANGE_INPUT_VALUE } from './actionTypes';
const defaultState = {
inputValue: '222',
list: ['ewewee', 'ewewe']
};
export default (state = defaultState, action) => {
if (action.type === CHANGE_INPUT_VALUE) {
const newState = JSON.parse(JSON.stringify(state));
newState.inputValue = action.value;
return newState;
}
return state;
}
// 컴포넌트에서 호출
import store from './store/index'
import { getInputChangeAction } from './store/actionCreators';
constructor(props) {
super(props);
console.log(store.getState())
this.state = { }
this.handleStoreChange = this.handleStoreChange.bind(this)
store.subscribe(this.handleStoreChange)
}
handleStoreChange() {
console.log(store.getState())
}
// 데이터 가져오기
store.getState()
// 데이터 추가
handleInputValue(event) {
const action = getInputChangeAction(event.target.value);
store.dispatch(action);
};
캐싱
데이터 캐시에 저장
localStorage.setItem('user', values.username);
캐시에서 데이터 가져오기
localStorage.getItem('user');
플러그인
npm i react-redux -S
미들웨어
npm i redux-thunk -S
// index.js (디버깅 도구 > REDUX_DEVTOOLS 설치)
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import reducer from './reducer';
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
const enhancer = composeEnhancers(applyMiddleware(thunk));
const store = createStore(reducer, enhancer);
export default store;
// actionCreators.js
import { CHANGE_INPUT_VALUE } from './actionTypes';
import axios from 'axios';
export const getInputChangeAction = (value) => ({
type: CHANGE_INPUT_VALUE,
value
});
export const getList = () => {
return (dispatch) => {
axios.get('/api/data', { params: { page: 1, limit: 10 } }).then((res) => {
const data = res.data.list;
const action = getInputChangeAction(data);
dispatch(action);
});
};
};
// 컴포넌트에서 호출
import store from './store/index';
import { getList } from './store/actionCreators';
fetchData() {
const action = getList();
store.dispatch(action);
}
npm i redux-saga -S
라우터 설치 (react-router-dom)
npm i react-router-dom -S
// index.js (엔트리 파일)
import React from 'react';
import ReactDOM from 'react-dom';
import 'antd/dist/antd.css';
import './index.css';
import Index from './view/common/index';
import { BrowserRouter } from 'react-router-dom';
ReactDOM.render(
<BrowserRouter>
<Index />
</BrowserRouter>,
document.getElementById('root')
);
// router/index.js
import React from 'react';
import { Switch, Route, Redirect } from 'react-router-dom';
import Index from '../view/index/index';
export default class Router extends React.Component {
render() {
return (
<Switch>
<Route path="/" exact render={() => <Redirect to="/index" />} />
<Route path="/index" component={Index} />
</Switch>
);
}
}
프로그래매틱 내비게이션:
this.props.history.push('/download');
외부 링크로 이동:
window.location.href = 'https://your-url';
데이터 요청 (axios)
npm i axios -S
package.json 설정:
"proxy": "http://api.example.com"
import React from 'react';
import { Table } from 'antd';
import axios from 'axios';
export default class UserList extends React.Component {
constructor(props) {
super(props);
this.state = {
data: []
};
}
componentDidMount() {
this.fetchData();
}
fetchData() {
axios.get('/backend/account/index', { params: { page: 1, limit: 10, token: 'your-token' } })
.then((res) => {
this.setState({ data: res.data.data.list });
})
.catch((error) => {
console.error(error);
});
}
render() {
return (
<Table dataSource={this.state.data} rowKey="id" />
);
}
}
Ant Design UI 설치
npm i antd -S
공식 문서: https://ant.design/docs/react/use-with-create-react-app-cn
개발 환경 설정
Ant Design에 맞게 설정 변경 (공식 문서 참고)
npm i react-app-rewired -D
npm i babel-plugin-import -D // 코드 스플리팅 (필요한 모듈만 로드)
기본 문법
// 최상위 태그 대체 플레이스홀더
<Fragment>
// 생성자
constructor(props) {
super(props);
this.state = { // 컴포넌트 상태
};
}
// 전개 연산자를 사용한 상태 업데이트
this.setState({
list: [...this.state.list, this.state.inputVal],
inputVal: ''
});
// 불변성 (Immutable)
// state는 직접 수정할 수 없습니다.
// HTML 이스케이프 해제
dangerouslySetInnerHTML={{ __html: item }}
// label 태그 사용
<label htmlFor="inputField">입력 내용</label>
<input id="inputField" className="input" value={this.state.inputVal} onChange={this.handleInputChange.bind(this)} />
<button onClick={this.handleSubmit.bind(this)}>제출</button>
코드 최적화
// 생성자에서 메서드 바인딩
this.handleSubmit = this.handleSubmit.bind(this);
// 리스트 렌더링을 위한 별도 메서드
renderList() {
return this.state.list.map((item, index) => {
return (
<ListItem
key={index}
content={item}
index={index}
onDelete={this.handleDelete}
/>
);
});
}
// 자식 컴포넌트
render() {
const { content } = this.props;
return (
<div onClick={this.handleClick}>
{content}
</div>
);
}
handleClick() {
const { onDelete, index } = this.props;
onDelete(index);
}
// ES6를 사용한 상태 업데이트
this.setState((prevState) => ({
list: [...prevState.list, prevState.inputVal],
inputVal: ''
}));
handleDelete(index) {
this.setState((prevState) => {
const list = [...prevState.list];
list.splice(index, 1);
return { list };
});
}
Props 타입 검증
npm install --save prop-types
import PropTypes from 'prop-types';
ListItem.propTypes = {
test: PropTypes.string.isRequired, // 필수 전달
test: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), // 여러 타입 허용
content: PropTypes.string,
onDelete: PropTypes.func,
index: PropTypes.number
};
// 기본값 설정
ListItem.defaultProps = {
test: 'hello'
};
디버깅 도구
React Developer Tools 및 Redux DevTools 설치 및 사용
Chrome 브라우저 => 설정 => 확장 프로그램 => Chrome 웹 스토어 열기 => "React Developer Tools" 검색 => 설치
=> 브라우저에서 개발자 도구 열기 => React 탭 확인