ạo ứng dụng ghi chú với ReactJS và Redux - ReactJS Example
Trong bài viết này mình sẽ hướng dẫn xây dựng một ứng dụng ghi chú đơn giản sử dụng ReactJS và Redux. Đây cũng là một project nhỏ giúp làm quen với Redux. Bạn cần chuẩn bị một vài kiến thức về Redux trước khi bắt tay vào xây dựng ứng dụng bằng cách tham khảo bài viết về tích hợp Redux vào ...
Trong bài viết này mình sẽ hướng dẫn xây dựng một ứng dụng ghi chú đơn giản sử dụng ReactJS và Redux. Đây cũng là một project nhỏ giúp làm quen với Redux.
Bạn cần chuẩn bị một vài kiến thức về Redux trước khi bắt tay vào xây dựng ứng dụng bằng cách tham khảo bài viết về tích hợp Redux vào ReactJS.
Ở đây ta sẽ xây dựng ứng dụng ghi chú đơn giản với chức năng bao gồm thêm, sửa, xóa ghi chú, có cấu trúc thư mục src
như sau:
src/ ....const/ index.js .....actions/ index.js ....reducers/ noteReducer.js index.js ----components/ AddNote.css AddNote.js ShowNote.js ....App.js
Ngoài ra mình có sử dụng thư viện Bootstrap 4 để xây dựng giao diện, hãy tham khảo thêm về Bootstrap 4 để có thể hiểu rõ hơn nhé.
Giao diện của ứng dụng ghi chú
1. Cài đặt thư viện cần thiết
Trước tiên cần phải cài đặt redux cho dự án của mình bằng npm với câu lệnh:
npm install redux react-redux --save
Để có thể xây dựng giao diện dễ dàng hơn ta sẽ sử dụng Bootstrap 4, tiến hành thêm thư viện bootstrap vào bên trong thẻ <head>
của file public/index.html
.
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">
Bước tiếp theo phải phân tích xem project cần những chức năng gì, để từ đó xây dựng cấu trúc redux một cách hợp lí hơn. Đối với project này bao gồm 3 chức năng chính đó là thêm, sửa, xóa ghi chú. Đồng nghĩa với đó là sẽ có 3 actions, 1 reducers và 3 const,..chúng ta sẽ triển khai ở bên dưới.
2. Triển khai Redux
Chúng ta sẽ triển khai mô hình Redux với 3 thư mục đó là actions, reducers, const,..Với từng thư mục sẽ có chức năng riêng.
Thư mục const
sẽ chứa các hằng số hỗ trợ trong quá trình triển khai dự án. Ta sẽ khai báo các hằng số ở trong file const/index.js
.
// const/index.js export const ADD_NEW_NOTE = "ADD_NEW_NOTE"; export const REMOVE_NOTE = "REMOVE_NOTE"; export const EDIT_NOTE = "EDIT_NOTE";
Thư mục actions
sẽ bao gồm các hàm chứa hành động để thực hiện dispatch tới reducers. Ở đây chúng ta sẽ triển khai các hàm ở file actions/index.js
.
import { ADD_NEW_NOTE, REMOVE_NOTE, EDIT_NOTE } from "../const/index"; //action thêm note export const actAddNote = (content) => { return { type: ADD_NEW_NOTE, content, }; }; //Xóa note export const actRemoveNote = (id) => { return { type: REMOVE_NOTE, id, }; }; //Sửa note export const actEditNote = (id, content) => { return { type: EDIT_NOTE, id, content, }; };
Có 3 hàm được export ở file actions/index.js
đó là actAddNote
, actRemoveNote
, actEditNote dùng để tạo action cho chức năng thêm, sửa, xóa note.
Thư mục reducers
sẽ chứa các hàm chứa các reducers trong Redux, có 2 files đó là
noteReducers.js
: reducers hỗ trợ thực thi hành động với ghi chú như thêm, sửa, xóa. Ta có thể tạo nhiều reducers như này, và gộp nó vào bằng phương thứccombineReducers
trong redux.index.js
: trong trường hợp có nhiều reducers thì gộp tất cả reducers ở đây.
//reducesr/noteReducers.js //Import các const import { ADD_NEW_NOTE, REMOVE_NOTE, EDIT_NOTE } from "../const/index"; const noteReducers = (state = [], action) => { switch (action.type) { case ADD_NEW_NOTE: const generateID = new Date().getTime(); state = [...state, { id: generateID, content: action.content }]; return state; case EDIT_NOTE: const indexOfEditNote = state.findIndex((note) => note.id === action.id); if (indexOfEditNote !== -1) state[indexOfEditNote].content = action.content; return state; case REMOVE_NOTE: const idRemove = action.id; state = state.filter(note => { if (note.id === idRemove) return false return true }) return state; default: return state; } }; export default noteReducers
//reducers/index.js import {combineReducers} from 'redux' import noteReducers from './noteReducer' export default combineReducers({ note: noteReducers })
Để các component khác có thể tương tác với Store ta cần bọc root component bởi Provider
. Ở file src/index.js
sẽ chỉnh sửa lại như sau.
import React from "react"; import ReactDOM from "react-dom"; import "./index.css"; import App from "./App"; import * as serviceWorker from "./serviceWorker"; import { Provider } from "react-redux"; import { createStore } from "redux"; //Gọi reducers import reducers from "./reducers/index"; //Tạo store từ reducers const store = createStore(reducers); ReactDOM.render( <React.StrictMode> <Provider store={store}> <App /> </Provider> </React.StrictMode>, document.getElementById("root") ); serviceWorker.unregister();
Vậy là các bước cài đặt redux xong, tiếp theo sẽ là đi xây dựng các component.
3. Xây dựng giao diện ứng dụng ghi chú
Bây giờ sẽ đi xây dựng giao diện và kết nối với store redux trong mỗi component. Ví dụ này sẽ có 2 components đó thêm và hiển thị ghi chú (nằm trong thư mục src/components
).
Ở đây 2 component AddNote
và ShowNote
sẽ được render ở trong file src/App.js
//file: src/App.js import React from "react"; import "./App.css"; //Kết nối vơi redux import { connect } from "react-redux"; import ShowNote from './components/ShowNote' import AddNote from './components/AddNote' function App(props) { return ( <div className="row" style={{ marginTop: "3%" }}> <AddNote /> {props.note.map((note, index) => { // Render ra lần lượt các ghi chú. return <ShowNote noteData = {note} key={note.id}/> })} </div> ); } // Lấy state từ store bằng mapStateToProps // Lúc này state nhận được sẽ gán vào props const mapStateToProps = (state, ownProps) => { // Gán state nhận về từ store // thành props có tên note (props.note) return { note: state.note, }; }; export default connect(mapStateToProps, null)(App);
Component AddNote
sẽ có chức năng thêm ghi chú mới, file AddNote.js
sẽ có nội dung như sau:
//file AddNote.js import React, { useState, useRef } from "react"; import { connect } from "react-redux"; //Import actions vào đây import { actAddNote } from "../actions/index"; function AddNote(props) { const [content, setContent] = useState(); //Refs: Giúp chúng ta tương tác với DOM thật const noteInput = useRef(null) //Khi click vào nút thêm sẽ gọi hàm này, const handleAdd = () => { // Dispatch action. // Props này được tạo bởi hàm // mapDispatchToProps bên dưới props.addNote(content) //Gán giá trị cho input thành rỗng noteInput.current.value = '' //Cập nhật lại state content setContent('') }; return ( <div className="col-md-12" style={{ marginBottom: 15 }}> <div className="input-group mb-8"> <input type="text" className="form-control" placeholder="Nội dung ghi chú" value = {content} onChange={(e) => { setContent(e.target.value) }} ref={noteInput} /> <div className="input-group-append"> <button className="btn btn-primary" onClick={handleAdd}> Thêm </button> </div> </div> </div> ); } //Chuyển dispatch thành props. //Ở đây nếu mình muốn dispatch action actAddNote //thì chỉ cần gọi props.addNote(content) const mapDispatchToProps = (dispatch) => { return { addNote: (content) => { dispatch(actAddNote(content)); }, }; }; export default connect(null, mapDispatchToProps)(AddNote);
Component ShowNote
sẽ nhận vào một props là dataNote
chứ các thuộc tính của từng ghi chú. Component này sẽ có chức năng sửa và xóa các ghi chú.
//file: src/components/ShowNote.js import React, { useState } from "react"; import "./ShowNote.css"; import { connect } from "react-redux"; import { actEditNote, actRemoveNote } from "../actions/index"; function ShowNote(props) { const [noteContent, setNoteContent] = useState(props.noteData.content) //Lấy ID của ghi chú const noteID = props.noteData.id // Được gọi mỗi khi thay đổi giá trị // của ghi chú. const handleChange = (e) => { setNoteContent(e.target.value) props.editNote(noteID, e.target.value) } //Được gọi khi click vào //xóa ghi chú. const handleRemoveNote = () => { props.removeNote(noteID) } return ( <div className="col-md-4" style={{marginTop: 10}}> <div className="card bg-warning"> <div className="card-body" style={{ height: 200 }}> <textarea value= {noteContent} onChange = {handleChange}></textarea> </div> <div className="card-footer"> <button className="btn btn-danger btn-sm float-right" onClick={handleRemoveNote}>Xóa</button> </div> </div> </div> ); } // chuyển dispatch thành props const mapDispatchToProps = (dispatch) => { return { editNote: (id, content) => { dispatch(actEditNote(id, content)); }, removeNote: id => { dispatch(actRemoveNote(id)); } }; }; // chuyển state từ store thành props // của component const mapStateToProps = (state, ownProps) => { return { note: state.note, }; }; export default connect(mapStateToProps, mapDispatchToProps)(ShowNote);
Để giao diện của component ShowNote
đẹp hơn 1 chút thì ta sẽ thêm một vài đoạn css ở file ShowNote.css
/* src/components/ShowNote.css */ textarea { background-color: rgba(0, 0, 0, 0); border-width: 0; overflow: hidden; resize: none; } textarea:focus{ outline: none; }
Đây cũng là buớc cuối cùng để xây dựng ứng dụng ghi chú, để khởi chạy dự án ta chỉ cần mở terminal và gõ dòng lệnh:
npm start
Trên đây là ví dụ về khởi tạo ứng dụng ghi chú sử dụng ReactJS và Redux, mong rằng ví dụ này sẽ giúp bạn có thể nắm chắc hơn về cách triển khai redux trong một dự án thực tế.