React router - Redux Form
Cài đặt redux form bằng command sau npm install --save redux-form Hoặc có thể cài một version nào đó mà bạn muốn npm install --save redux-form@4.1.3 Tuỳ thuộc vào từng version của redux-form mà cách viết viết form, field hoặc gọi action creator khác nhau, nếu cài đặt version mới mà code ...
Cài đặt redux form bằng command sau
npm install --save redux-form
Hoặc có thể cài một version nào đó mà bạn muốn
npm install --save redux-form@4.1.3
Tuỳ thuộc vào từng version của redux-form mà cách viết viết form, field hoặc gọi action creator khác nhau, nếu cài đặt version mới mà code sử dụng theo version cũ thì bạn sẽ có thể gặp một vài lỗi khó hiểu mặc dù làm đúng mà không chạy.
Điều đầu tiên sau khi cài đặt redux-form là cần phải móc Form reducer vào root reducer của app để Redux có thể quản lý state của form.
reducers/index.js
... import { reducer as formReducer } from 'redux-form'; const rootReducer = combineReducers({ form: formReducer }); export default rootReducer;
posts_new.js
class PostsNew extends Component { render() { <div> create posts </div> } } export default reduxForm({ form: 'PostsNewForm', fields: [ 'title', 'categories', 'content' ] })(PostsNew);
Component chứa form phải được wrap bởi Redux-Form, trong đó form name là PostsNewForm phải unique, tiếp đến là các input field name mà bạn sẽ đặt trong form. ReduxForm sẽ handle các input của form dựa vào khai báo này. ReduxForm sẽ dùng global state đẻ lưu lại bất cứ các thay đổi nào của input theo từng form name ở đây là PostsNewForm
render method của posts_new.js
const { fields: {title, categories, content}, handleSubmit } = this.props; return( <form onSubmit={handleSubmit(this.onSubmit.bind(this))}> <div>Create a new post.</div> <div className={`form-group ${title.touched && title.invalid ? 'has-danger' : '}`}> <label>Title</label> <input type="text" className="form-control" {...title} /> <div className="text-help"> {title.touched ? title.error : '} </div> </div> <div className={`form-group ${categories.touched && categories.invalid ? 'has-danger' : '}`}> <label>category</label> <input type="text" className="form-control" {...categories} /> <div className="text-help"> {categories.touched ? categories.error : '} </div> </div> <div className={`form-group ${content.touched && content.invalid ? 'has-danger' : '}`}> <label>content</label> <input type="area" className="form-control" {...content} /> <div className="text-help"> {content.touched ? content.error : '} </div> </div> <button type="submit" className="btn btn-primary">Submit</button> <Link to="/" className="btn btn-danger">Cancel</Link> </form>
Trong đó handleSubmit là một helper của ReduxForm, bất cứ khi nào user nhấn vào submit form thì helper này sẽ được gọi.
<input type="text" className="form-control" {...title} />
Ở trên ta đã truyền title configuration object vào input form. Mọi event như change, onBlur.. của input sẽ được object này đảm nhiệm và connect tới Redux Store.
Validate
function validate(values) { const errors = {}; if (!values.title) { errors.title = 'Enter your title' } if (!values.categories) { errors.categories = 'Enter your categories' } if (!values.content) { errors.content = 'Enter your content' } return errors; }
Validate function cần phải được wrap vào trong reduxFrom, values paramter là một object wrap các configuration input object
export default reduxForm({ form: 'PostsNewForm', fields: [ 'title', 'categories', 'content' ], validate })(PostsNew);
Context
Sử dụng Link component có thể giúp bạn chuyển đến một trang khác khi click vào một link, nhưng trong trường hợp khi muốn nhấn vào nút submit và sau khi submit thành công redirect về trang khác như thế nào? bạn không thể dùng Link component trong trường hợp này. Router có một hàm push giúp bạn có thể chuyển trang, và để sử dụng được nó cần phải có gọi context của router parent component.
Trong react bạn có thể truyền props từ component cha xuống component con một cách dễ dàng và tường minh Như ví dụ bên dưới PostsNew được truyền vào một cách tường minh
<Route path="posts/new" component={PostsNew} />
Thế nhưng đối với context thì khác các component con có thể gọi được info của component cha, component con sẽ look up cho đến khi tìm thấy context của component cha cần gọi.
Giả sử trong posts_new.js
class PostsNew extends Component { static contextTypes = { router: PropTypes.object } onSubmit(props) { this.props.createPost(props, () => this.context.router.push('/')); } }
Hàm onSubmit trên đảm nhận nhiệm vụ sumit dữ liệu form lên api và đồng thời sau khi POST thành công sẽ redirect từ trang new về trang root. push là một phương thức của react-router nhưng có thể được dùng ở PostsNew component là nhờ có context.
Trong tất cả các trường hợp ngoài lấy context của Router ra thì Facbook khuyến cáo không nên sử dụng context bởi nó là một phần của Flux và có thể bị thay thế trong các version tiếp theo. Chi tiết bạn có thể tham khảo tại đây https://facebook.github.io/react/docs/context.html
Bạn có thể xem code đầy đủ tại đây https://github.com/khanhhd/ReduxSimpleStarter-1/tree/sesson-6-routes
https://www.udemy.com/react-redux
http://redux-form.com/6.0.0-rc.3/
http://www.davidmeents.com/create-redux-form-validation-initialized-values/