Todo Reactjs và thay đổi Location

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ở

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.

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

Demo

Bắt đầu với reactjs app

npx create-react-app learn-reactjs
cd learn-reactjs
npminstall --save react-hook-form
npminstall --save react-router-dom
npminstall --save  @material-ui/core
npminstall -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-vitalsreportWebVitals();

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';functionApp(){return(<div className="app"><Switch><Redirect from="home" to="/" exact /><Route path="/todos" component={TodoFeature}/><Route component={NotFound}/></Switch></div>);}exportdefault 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 ={};functionNotFound(props){return(<div>
            Not Found
        </div>);}exportdefault 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,};functionInputField(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>);}exportdefault InputField;

todo

Chỉnh sửa file index.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",},}))functionTodoFeature(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>);}exportdefault 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,};functionTodoForm(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),})consthandleSubmit=(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>);}exportdefault 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,}functionTodoList(props){const classes =useStyles();const{ todoList, onTodoClick }= props;consthandlerTodoClick=(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>);}exportdefault 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),},}))functionListPage(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ạiconst[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 useEffectconsthandlerTodoClick=(todo, id)=>{//clone current array to the new arrayconst newTodoList =[...todoList]//toggle statelet 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 listsetTodoList(newTodoList)}consthandleShowAllClick=()=>{const queryParams ={ status:'all'}
        history.push({
            pathname: match.path,
            search: queryString.stringify(queryParams),})}consthandleShowCompletedClick=()=>{const queryParams ={ status:'completed'}
        history.push({
            pathname: match.path,
            search: queryString.stringify(queryParams),})}consthandleShowNewClick=()=>{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])consthandleTodoFormSubmit=(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>);}exportdefault ListPage;

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

Nguồn: viblo.asia

Bài viết liên quan

WebP là gì? Hướng dẫn cách để chuyển hình ảnh jpg, png qua webp

WebP là gì? WebP là một định dạng ảnh hiện đại, được phát triển bởi Google

Điểm khác biệt giữa IPv4 và IPv6 là gì?

IPv4 và IPv6 là hai phiên bản của hệ thống địa chỉ Giao thức Internet (IP). IP l

Check nameservers của tên miền xem website trỏ đúng chưa

Tìm hiểu cách check nameservers của tên miền để xác định tên miền đó đang dùn

Mình đang dùng Google Domains để check tên miền hàng ngày

Từ khi thông báo dịch vụ Google Domains bỏ mác Beta, mình mới để ý và bắt đầ