メモアプリ完成版ということで、
全部のコードを載せておきたいと思います。
動かない場合や参考になれば幸いです。
*ディレクトリ「src」以下のファイルの編集になります。
目次
ファイル名
action/index.js
// アクション定義
export const READ_MEMOS = "READ_MEMOS";
export const READ_MEMO = "READ_MEMO";
export const ADD_MEMO = "ADD_MEMO";
export const UPDATE_MEMO = "UPDATE_MEMO";
export const DELETE_MEMO = 'DELETE_MEMO';
// 一覧取得
export const getMemos = () => ({
type: READ_MEMOS,
});
// 一件取得
export const getMemo = (id) => ({
type : READ_MEMO,
params : id,
})
// メモの新規作成
export const addMemo = (values) => ({
type : ADD_MEMO,
params : values,
});
// 更新
export const updateMemo = (id, values) => ({
type : UPDATE_MEMO,
id : id,
params : values,
});
// 削除
export const deleteMemo = (id) => ({
type : DELETE_MEMO,
id : id,
});
reducers/index.js
import { combineReducers } from 'redux';
import { reducer as form } from 'redux-form';
import memos from './memo';
export default combineReducers({ memos, form });
reducers/memo.js
import { v4 as uuidv4 } from 'uuid';
import {
READ_MEMOS,
READ_MEMO,
ADD_MEMO,
UPDATE_MEMO,
DELETE_MEMO,
} from '../actions';
export default (memos = {}, action) => {
switch (action.type) {
case READ_MEMOS:
return memos;
case READ_MEMO:
return memos;
case ADD_MEMO:
var uid = uuidv4();
const insertData = {
id : uid,
title : action.params.title,
memo : action.params.memo,
}
return {...memos, [uid] : insertData};
case UPDATE_MEMO:
const updateData = {
id : action.id,
title : action.params.title,
memo : action.params.memo,
}
return {...memos, [updateData.id] : updateData};
case DELETE_MEMO:
delete memos[action.id];
return {...memos};
default:
return memos;
}
}
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
// import { createStore } from 'redux';
import { Switch, BrowserRouter, Route } from 'react-router-dom';
import { PersistGate } from 'redux-persist/integration/react';
import store, { persistor } from './configStore';
import reducer from './reducers';
import * as serviceWorker from './serviceWorker';
import MemoIndex from './components/memoIndex';
import MemoNew from './components/memoNew';
import MemoShow from './components/memoShow';
// const store = createStore(reducer);
ReactDOM.render(
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<BrowserRouter>
<Switch>
<Route path="/new" component={MemoNew} />
<Route path="/show/:id" component={MemoShow} />
<Route exact path="/" component={MemoIndex}/>
</Switch>
</BrowserRouter>
</PersistGate>
</Provider>,
document.getElementById('root')
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
components/memoIndex.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { getMemos } from '../actions';
import _ from 'lodash';
class MemoIndex extends Component {
componentDidMount() {
this.props.getMemos();
}
renderMemos = () => {
const memos = this.props.memos;
return _.map(memos, memo =>(
<tr key={memo.id}>
<td>
<Link to={`/show/${memo.id}`}>
{memo.title}
</Link>
</td>
<td>{memo.memo}</td>
</tr>
));
}
render() {
return (
<React.Fragment>
<table>
<thead>
<tr>
<th>タイトル</th>
<th>メモ</th>
</tr>
</thead>
<tbody>
{this.renderMemos()}
</tbody>
</table>
<Link to="/new">追加</Link>
</React.Fragment>
);
}
}
const mapStateToProps = state => ({ memos : state.memos });
const mamDispatchToProps = ({ getMemos });
export default connect(mapStateToProps, mamDispatchToProps)(MemoIndex);
conponents/memoShow.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Field, reduxForm } from 'redux-form';
import { Link } from 'react-router-dom';
import { getMemo, updateMemo, deleteMemo } from '../actions';
class memoShow extends Component {
componentDidMount() {
const {id} = this.props.match.params;
if (id) this.props.getMemo(id);
}
renderField = (field) => {
const {input, label, type, meta : {touched, error}} = field;
return (
<div>
<input {...input} placeholder={label} type={type} />
{touched && error && <span>{error}</span>}
</div>
);
}
onSubmit = (values) => {
const {id} = this.props.match.params;
this.props.updateMemo(id, values);
this.props.history.push('/');
}
onDeleteClick = () => {
const { id } = this.props.match.params;
this.props.deleteMemo(id);
this.props.history.push('/');
}
render() {
const {handleSubmit, pristine, submitting, invalid} = this.props;
console.log(submitting);
return (
<form onSubmit={handleSubmit(this.onSubmit)}>
<div><Field label="タイトル" name="title" type="text" component={this.renderField} /></div>
<div><Field label="メモ" name="memo" type="text" component={this.renderField} /></div>
<div>
<div><input type="submit" value="更新" disabled={pristine || submitting || invalid} /></div>
<div><Link to="/">キャンセル</Link></div>
<div><Link to="/" onClick={this.onDeleteClick}>削除</Link></div>
</div>
</form>
);
}
}
const validate = values => {
const errors = {};
if (!values.title) errors.title = 'タイトルが入力されていません。';
if (!values.memo) errors.memo = 'メモを入力してください。';
return errors;
}
const mapStateToProps = (state, ownProps) => {
const memo = state.memos[ownProps.match.params.id];
return { initialValues : memo }
};
const mapDispatchToProps = ({ getMemo, updateMemo, deleteMemo });
export default connect(mapStateToProps, mapDispatchToProps)(
reduxForm({validate, form: 'memoShowForm', enableReinitialize: true})(memoShow)
);
compopnents/memoNew.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Field, reduxForm } from 'redux-form';
import { Link } from 'react-router-dom';
import { addMemo } from '../actions';
class memoNew extends Component {
renderField = (field) => {
const {input, label, type, meta:{touched, error} } = field;
return (
<div>
<input {...input} placeholder={label} type={type} />
{touched && error && <span>{error}</span>}
</div>
);
}
onSubmit = (values) => {
this.props.addMemo(values);
this.props.history.push('/');
}
render() {
const { handleSubmit, pristine, submitting, invalid } = this.props;
return (
<form onSubmit={handleSubmit(this.onSubmit)}>
<div>
<Field label="タイトル" name="title" type="text" component={this.renderField} />
</div>
<div>
<Field label="メモ" name="memo" type="text" component={this.renderField} />
</div>
<div>
<div><input type="submit" value="追加" disabled={pristine || submitting || invalid } /></div>
<div><Link to="/">キャンセル</Link></div>
</div>
</form>
);
}
}
const validate = values => {
const errors = {};
if (!values.title) errors.title = 'タイトルが入力されていません。';
if (!values.memo) errors.memo = 'メモを入力してください。';
return errors;
}
const mapDispatchToProps = ({ addMemo });
export default connect(null, mapDispatchToProps)(
reduxForm({validate, form: 'memoNewForm'})(memoNew)
);
configStore.js
import { createStore } from 'redux';
import { persistReducer, persistStore } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import rootReducer from './reducers';
const persistConfig = {
key: 'root',
storage,
whitelist: ['memos']
};
const persistedReducer = persistReducer(persistConfig, rootReducer);
const store = createStore(
persistedReducer,
);
export const persistor = persistStore(store);
export default store;
コメント