Defining Saga

We are going to use the same endpoint to list users and search users. Searching users is similar to listing users with search queries. To list users, follow these steps:

  1. Define Saga. The Saga file is placed inside app/containers/User/saga.js. Here, we have only included the Saga code to create a user. The rest of the Saga code can be found inside CH06/app/containers/User/saga.js. It contains Saga for searching the user, removing a user, updating a user, creating a user, and checking a user's details:
import request from 'utils/request';
import qs from 'query-string';
import { notification } from 'antd';
import { call, put, takeLatest } from 'redux-saga/effects';
import {
USER_CREATE_REQUEST
} from './constants';
import {
onCreateSuccess,
onCreateFailure,
} from './actions';

export function* onCreateRequest(action) {
try {
const { success, user, message } = yield call(request, `/api/users`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ user: action.item }),
});

if (!success) {
throw message;
}

notification.success({
message: 'Create user ssucessfully',
description: `${user.name} was created`,
});
action.cb && action.cb(user);
yield put(onCreateSuccess(user));
} catch (err) {
notification.error({
message: 'Create user unssucessfully',
description: err.toString(),
});

yield put(onCreateFailure(err.toString()));
}
}
export default function* data() {
yield takeLatest(USER_CREATE_REQUEST, onCreateRequest);
}
  1. Define reducers. The reducer file to search, remove, update and search users can be found in CH06/app/containers/User/reducer.js. In the next chapter, we are going to check how to debug each of the requests. 
  1. Inject the reducers and the Saga to the root user container:
import React from 'react';
import { compose } from 'redux';
import { Switch, Route } from 'react-router-dom';
import injectSaga from 'utils/injectSaga';
import injectReducer from 'utils/injectReducer';
import saga from './saga';
import reducer from './reducer';
import All from './All';
import AddUser from './Add';
import EditUser from './Edit';

const User = () => (
<Switch>
<Route exact path="/" component={All} />
<Route exact path="/users/add" component={AddUser} />
<Route path="/users/:id/edit" component={EditUser} />
</Switch>
);

const withSaga = injectSaga({ key: 'user', saga });
const withReducer = injectReducer({ key: 'user', reducer });

export default compose(
withSaga,
withReducer,
)(User);
  1. Connect the User container with Redux. The container file is updated to container code snippet, given as follows:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import qs from 'query-string';
import styled from 'styled-components';
import { Pagination } from 'antd';
import { createStructuredSelector } from 'reselect';
import {
getUsers,
getTotalPage,
getTotalItem,
getCurrentPage,
getDeleteItem,
} from './selectors';
import Form from './Form';
import List from './List';
import { onSearchRequest, onRemoveRequest } from './actions';

const PaginationView = styled.div`
margin: 40px 0;
`;

class User extends Component {
componentDidMount() {
this.props.onSubmit(qs.parse(this.props.location.search));
}

componentWillReceiveProps(nextProps) {
const newProps = qs.parse(nextProps.location.search);
const oldProps = qs.parse(this.props.location.search);

if (
oldProps.s !== newProps.s ||
oldProps.page !== newProps.page ||
nextProps.deleting !== this.props.deleting
) {
this.props.onSubmit(newProps);
}
}

onSubmit = e => {
this.props.history.push({
search: qs.stringify({
...qs.parse(this.props.location.search),
...e.toJS(),
page: 1,
}),
pathname: this.props.history.location.pathname,
});
};

onChange = page => {
this.props.history.push({
search: qs.stringify({
...qs.parse(this.props.location.search),
page,
}),
pathname: this.props.history.location.pathname,
});
};

render() {
const { results, totalPage, totalItem, currentPage, location } = this.props;
const newProps = qs.parse(location.search);

return (
<div className="all-user-containers">
<Form onSubmit={this.onSubmit} initialValues={newProps} />
<List
dataSource={results}
keyword={newProps.s}
onRemove={this.props.onRemove}
onReload={() => this.props.onSubmit(newProps)}
/>
<PaginationView>
{totalPage > 0 && (
<Pagination
style={{ marginTop: 10 }}
current={currentPage + 1}
total={totalItem}
pageSize={10}
onChange={this.onChange}
/>
)}
</PaginationView>
</div>
);
}
}

export const mapStateToProps = createStructuredSelector({
results: getUsers(),
totalPage: getTotalPage(),
totalItem: getTotalItem(),
deleting: getDeleteItem(),
currentPage: getCurrentPage(),
});

export const mapDispatchToProps = dispatch => ({
onSubmit: s => dispatch(onSearchRequest(s)),
onRemove: s => dispatch(onRemoveRequest(s)),
});

export default connect(
mapStateToProps,
mapDispatchToProps,
)(User);
  1. Connect the AddUser component to Redux:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import Form from './UserForm';
import { onCreateRequest } from './actions';

class AddUser extends Component {
onSubmit = e => {
this.props.onCreate(e.toJS(), () => this.props.history.push('/'));
};

render() {
return (
<div className="add-user-containers">
<Form
isNew
onSubmit={this.onSubmit}
initialValues={{ role: 'user', gender: 'male' }}
caption="Add New User"
/>
</div>
);
}
}

AddUser.propTypes = {
onCreate: PropTypes.func.isRequired,
};

export const mapDispatchToProps = dispatch => ({
onCreate: (item, cb) => dispatch(onCreateRequest(item, cb)),
});

export default withRouter(
connect(
null,
mapDispatchToProps,
)(AddUser),
);
  1. Connect the Edit User page to Redux:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import { createStructuredSelector } from 'reselect';
import Form from './UserForm';

import { onUpdateRequest, onDetailRequest } from './actions';
import { getUser } from './selectors';

class EditUser extends Component {
componentDidMount() {
this.props.onFetch(this.props.match.params.id);
}

componentWillReceiveProps(nextProps) {
if (this.props.match.params.id !== nextProps.match.params.id) {
this.props.onFetch(nextProps.match.params.id);
}
}

onSubmit = e => {
this.props.onUpdate(this.props.match.params.id, e.toJS(), () =>
this.props.history.push('/'),
);
};

render() {
const { user } = this.props;

return (
<div className="add-user-containers">
<Form
initialValues={user}
onSubmit={this.onSubmit}
caption="Edit User"
/>
</div>
);
}
}

EditUser.propTypes = {
user: PropTypes.object,
onFetch: PropTypes.func.isRequired,
onUpdate: PropTypes.func.isRequired,
};

export const mapStateToProps = createStructuredSelector({
user: getUser(),
});

export const mapDispatchToProps = dispatch => ({
onFetch: id => dispatch(onDetailRequest(id)),
onUpdate: (id, item, cb) => dispatch(onUpdateRequest(id, item, cb)),
});

export default withRouter(
connect(
mapStateToProps,
mapDispatchToProps,
)(EditUser),
);

This is should give us a working application in which we can add a new user, delete a user, edit a user, and view the details of a user. 

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset