- vừa được xem lúc

Todo Reactjs và thay đổi Location

0 0 25

Người đăng: Kính Gia Hà

Theo Viblo Asia

Giới thiệu

Reatcjs là một thư viện JavaScript front-end mã nguồn mở và miễn phí để xây dựng giao diện người dùng hoặc các thành phần UI. Nó được duy trì bởi Facebook và một cộng đồng các nhà phát triển và công ty cá nhân. React có thể được sử dụng làm cơ sở để phát triển các ứng dụng một trang hoặc ứng dụng di động. Tuy nhiên, React chỉ quan tâm đến việc quản lý trạng thái và hiển thị trạng thái đó cho DOM , vì vậy việc tạo các ứng dụng React thường yêu cầu sử dụng các thư viện bổ sung để định tuyến, cũng như một số chức năng phía máy khách nhất định. image.png

Quá trình

Trong quá trình học reactjs. Tôi đã học rất nhiều khóa học cơ bản nhưng khi không code một vài tuần tôi lại quên và mơ hồ về các định nghĩa và cách sử dụng, cũng như phân chia components, và các file sao cho nó code logic và hợp lý. Với reactjs là một thư viện, và trong đó có quá nhiều thứ đề lựa chọn. Nó như một bàn ăn và trên đó có cực kỳ nhiều món ăn, nào là axios, react-router-dom, redux-toolkit, react-hook-form, ant design, material ui,... Khi tôi mới vào học kiểu tôi bị ngợp với đống món ăn đây. Nhưng sau quá trinh tìm hiểu và được chỉ bảo thì mình đã hiểu khá rõ về nó. Lời khuyên của mình cho các bạn là nên học javascript nâng cao trước, nói nâng cao nhưng cũng không phải nâng cao mà là nắm chắc về cơ bản và học về es6 và nhiều kiến thức javascript nữa. Qua đống mình cũng nên học và biết chút ít về backend để phục vụ cho việc học về fetch api.

Bắt đầu với todolist

Bắt đầu với reactjs app

npx create-react-app learn-reactjs
cd learn-reactjs
npm install --save react-hook-form
npm install --save react-router-dom
npm install --save @material-ui/core
npm install -g sass
npm start

Trong app reactjs mình sẽ tạo các thư mục sau đây.

learn-reactjs
├─ build
├─ node_modules
├─ public
├─ src
│ ├─ components
│ │ ├─ form-control
│ │ │ └─ InputField
│ │ │ └─ index.jsx
│ │ └─ NotFound
│ │ └─ index.jsx
│ └─ features │ └─ Todo
│ ├─ components
│ │ ├─ TodoForm
│ │ │ └─ index.jsx
│ │ └─ TodoList │ │ ├─ index.jsx
│ │ └─ style.scss
│ └─ pages
│ │ └─ ListPage
│ │ └─ index.jsx
│ └─ index.jsx
├─ App.css
├─ App.js
└─ index.js

Chỉnh sửa file

index.js

Chỉnh sửa fille index.js trong learn-reactjs

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import App from './App';
import './index.css';
import reportWebVitals from './reportWebVitals'; ReactDOM.render( <React.StrictMode> <BrowserRouter> <App /> </BrowserRouter> </React.StrictMode>, document.getElementById('root')
); // If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals(); 

app.js

Chỉnh sửa fille app.js trong learn-reactjs

import { Redirect, Route, Switch } from 'react-router-dom';
import './App.css';
import NotFound from './components/NotFound';
import TodoFeature from './features/Todo'; function App() { return ( <div className="app"> <Switch> <Redirect from="home" to="/" exact /> <Route path="/todos" component={TodoFeature} /> <Route component={NotFound} /> </Switch> </div> );
} export default App; 

chỉnh sửa app.css trong file learn-reactjs

html{ background-color: rgba(244,244,244);
}

NotFound

Chỉnh sửa file index.jsx trong notfound

import React from 'react'; NotFound.propTypes = {
}; function NotFound(props) { return ( <div> Not Found </div> );
} export default NotFound;

form-control

Chỉnh sửa file index.jsx trong InputField. Ở đây chúng ta sử dung Controler để quản lý việc submit form. Khi form được submit thì dữ liệu được control của Controler quản lý sẽ gửi lên thằng cha truyền cho thằng con InputField. Đồng thời ở đây ta có formState lấy errors ra. Nếu có lỗi thì xác định lỗi và hiển thị cho người dùng.

import { TextField } from '@material-ui/core';
import PropTypes from 'prop-types';
import React from 'react';
import { Controller } from "react-hook-form";
import { FormHelperText } from '@material-ui/core'; InputField.propTypes = { form: PropTypes.object.isRequired, name: PropTypes.string.isRequired, label: PropTypes.string, disabled: PropTypes.bool,
}; function InputField(props) { const { form, name, label, disabled } = props const { formState: { errors } } = form const hasError = errors[name] return ( <div> <Controller control={form.control} name={name} render={({ field }) => ( <TextField {...field} fullWidth margin="normal" variant="outlined" label={label} disabled={disabled} error={!!hasError} /> )} /> <FormHelperText error={!!hasError}> {errors[name]?.message} </FormHelperText> </div> );
} export default InputField;

todo

Chỉnh sửa file jsx trong todo. Ở đây chúng ta có sử dụng material-ui bạn có thể tham khảo tại đây để biết các sử dụng. Chúng ta sử dụng useRouteMatch() để lấy đường dẫn hiện tại.

import { makeStyles } from '@material-ui/core';
import React from 'react';
import { Route, Switch, useRouteMatch } from 'react-router-dom';
import NotFound from '../../components/NotFound';
import DetailPage from './pages/DetailPage';
import ListPage from './pages/ListPage'; const useStyles = makeStyles((theme) => ({ root: { textAlign: "center", padding: "0 0 150px 0", }, header: { fontWeight: "200", fontSize: "100px", color: "#3f50b5", },
})) function TodoFeature(props) { const classes = useStyles(); const match = useRouteMatch(); return ( <div className={classes.root}> <h1 className={classes.header}>Todo share UI</h1> <Switch> <Route path={match.path} component={ListPage} exact/> <Route component={NotFound} /> </Switch> </div> );
} export default TodoFeature;

todo-form

Chỉnh sửa file index.jsx trong todo-form. Tại đây chúng ta cũng chỉnh sửa giao diện bằng material-ui. Ở đây chúng ta tạo ra useForm để lấy dữ liệu và schema để xác định lỗi cho form. Bạn có thể tìm hiểu ở react-hook-form. chúng ta có handleSubmit() để nhận dữ liệu từ form và xử lý khi form.handleSubmit được gọi.

import React from 'react';
import PropTypes from 'prop-types';
import InputField from '../../../../components/form-control/InputField';
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from "yup";
import { makeStyles } from '@material-ui/core'; const useStyles = makeStyles((theme) => ({ root: { width: "40%", margin: "0 auto", },
})) TodoForm.propTypes = { onSubmit: PropTypes.func,
}; function TodoForm(props) { const classes = useStyles() const { onSubmit } = props const schema = yup.object().shape({ title: yup.string().required("Please enter title") .min(3, "Title is too short"), }); const form = useForm({ defaultValues: { title: '', }, resolver: yupResolver(schema), }) const handleSubmit = (values) => { if (onSubmit) { onSubmit(values) } form.reset() } return ( <div className={classes.root}> <form onSubmit={form.handleSubmit(handleSubmit)}> <InputField name="title" label="Todo" form={form} /> </form> </div> );
} export default TodoForm;

todo-list

Chỉnh sửa file index.jsx trong todo-list. Tại đây chúng ta nhận từ thằng cha 2 tham số đó là todoList(list), onTodoClick(function). Khi item nào trong danh sách được click thì chúng ta gọi handlerTodoClick và hàm này sẽ gọi onTodoClick và truyền dữ liệu từ thằng cha truyền vào để thằng cha xử lý.

import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import './styles.scss';
import { makeStyles } from '@material-ui/core'; const useStyles = makeStyles((theme) => ({ root: { },
})) TodoList.propTypes = { todosList: PropTypes.array, onTodoClick: PropTypes.func,
}; TodoList.defaultProps = { todosList: [], onTodoClick: null,
} function TodoList(props) { const classes = useStyles(); const { todoList, onTodoClick } = props; const handlerTodoClick = (todo, id) => { if(!onTodoClick) return ; onTodoClick(todo, id); } return ( <div className={classes.root}> <ul className="todo-list"> {todoList.map((todo,index) => ( <li className={classnames({ 'todo-item':true, completed: todo.status === 'completed' })} key={todo.id} onClick={() => handlerTodoClick(todo, todo.id)} > {todo.title} </li> ))} </ul> </div> );
} export default TodoList;

ListPage

Chỉnh sửa file index.jsx trong ListPage.Ở đây chúng ta tạo ra state todoList để lưu state với useState . Tại đấy chúng ta viết hàm handlerTodoClick để xử lý dữ liệu từ thằng con gửi lên. Chúng ta cũng tạo một state filterStatus để tìm kiếm các state của todo(ví dụ: Hoàn thành, chưa hoàn thành, ...) đồng thời sử dụng history để cập nhật đượng dẫn ở location. Chúng tat sử dụng useLocation() để lấy location hiện tại. và useRouteMatch() để lấy pathname hiện tại để phục vụ cho nhu cầu update location.

import { Button, makeStyles } from '@material-ui/core';
import queryString from 'query-string';
import React, { useEffect, useMemo, useState } from 'react';
import { useHistory, useLocation, useRouteMatch } from 'react-router-dom';
import TodoForm from '../../components/TodoForm';
import TodoList from '../../components/TodoList'; const useStyles = makeStyles((theme) => ({ button: { margin: theme.spacing(4, 2), },
})) function ListPage(props) { const initTodoList = [ { id: 1, title: 'Eat', status: 'new', }, { id: 2, title: 'Sleep', status: 'completed', }, { id: 3, title: 'Code', status: 'new', }, { id: 4, title: 'Play Game', status: 'completed', }, ] const classes = useStyles(); const location = useLocation(); //lấy location hiện tại. location.search = ?status= const history = useHistory(); //lấy history để thay đổi location hiện tại.  const match = useRouteMatch(); //sử dụng mathc để lấy path name hiện tại const [todoList, setTodoList] = useState(initTodoList); const [filterStatus, setFilterStatus] = useState(() => { const params = queryString.parse(location.search); return params.status || 'all'; }); useEffect(() => { const params = queryString.parse(location.search); setFilterStatus(params.status || 'all') }, [location.search]) //khi location hiện tại hay đổi thì mình sẽ thực hiện useEffect const handlerTodoClick = (todo, id) => { //clone current array to the new array const newTodoList = [...todoList] //toggle state let toggleTodo = null; let index = 0; for (let i = 0; i < newTodoList.length; i++) { if (newTodoList[i].id === id) { toggleTodo = newTodoList[i]; index = i; break; } } const newTodo = { ...newTodoList[index], status: toggleTodo.status === 'new' ? 'completed' : 'new', } newTodoList[index] = newTodo //update todo list setTodoList(newTodoList) } const handleShowAllClick = () => { const queryParams = { status: 'all' } history.push({ pathname: match.path, search: queryString.stringify(queryParams), }) } const handleShowCompletedClick = () => { const queryParams = { status: 'completed' } history.push({ pathname: match.path, search: queryString.stringify(queryParams), }) } const handleShowNewClick = () => { const queryParams = { status: 'new' } history.push({ pathname: match.path, search: queryString.stringify(queryParams), }) } const renderTodoList = useMemo(() => { return todoList.filter(todo => filterStatus === 'all' || filterStatus === todo.status); }, [todoList, filterStatus]) const handleTodoFormSubmit = (values) => { console.log(values) const newTodo = { id: todoList.length+1, title: values.title, status: 'new' } const newTodoList = [...todoList, newTodo] setTodoList(newTodoList) } return ( <div> <TodoForm onSubmit={handleTodoFormSubmit}/> <TodoList todoList={renderTodoList} onTodoClick={handlerTodoClick} /> <Button className={classes.button} color="primary" variant="contained" onClick={handleShowAllClick}>Show All</Button> <Button className={classes.button} color="primary" variant="contained" onClick={handleShowCompletedClick}>Show Completed</Button> <Button className={classes.button} color="primary" variant="contained" onClick={handleShowNewClick}>Show New</Button> </div> );
} export default ListPage;

Đến đây là đã hoàn thành dự án TodoList rồi. Chúc các bạn thành công

Bình luận

Bài viết tương tự

- vừa được xem lúc

Tìm hiểu về Virtual DOM trong React

Nếu bạn đang dùng React hoặc đang học ReactJS, chắc hẳn bạn đã nghe qua thuật ngữ Virtual DOM . Vậy Virtual DOM là gì và tại sao React lại sử dụng nó. Chúng ta hãy cùng tìm hiểu nhé. Let's go.

0 0 466

- vừa được xem lúc

10 câu hỏi phỏng vấn React cơ bản dành cho các developer

1. Ưu nhược điểm của React. Ưu điểm:. .

0 0 88

- vừa được xem lúc

Tìm hiểu về ESlint và cách cấu hình trong React

Eslint là gì . vậy Eslint được tạo ra để làm gì .

0 0 310

- vừa được xem lúc

Hướng dẫn cấu hình ReactJS với Webpack và Babel

Ok trong bài viết này, mình sẽ hướng dẫn các bạn cấu hình dự án ReactJS kết hợp Webpack và Babel. Bài viết này được thực hiện năm 2021 được cấu hình trên Webpack 5, như các bạn biết thì các bài viết c

0 0 125

- vừa được xem lúc

Tips and tricks trong ReactJS

Dưới đây sẽ là một số thủ thuật tuyệt vời mà bạn có thể áp dụng để cải thiện chất lương project React của mình. Hãy áp dụng những thủ thuật này trong project React của bạn ngay hôm nay thôi nào.

0 0 124

- vừa được xem lúc

Tìm hiểu về Reactjs căn bản

Chắc hẳn "React" đã không còn là từ mới lạ đối với các bạn nữa vì sự phổ biến của nó, đã có nhiều "đàn anh" đi trước như : Angular, Backbone,... Thế nhưng sự cạnh tranh của React là không hề kém cạnh,

0 0 40