Thumbnail
Category: Lập trình

Kinh nghiệm React

Date: August 28, 2020
57 views


React là thư viện JavaScript phổ biến nhất để xây dựng giao diện người dùng (UI). Nó cho tốc độ phản hồi tuyệt vời khi user nhập liệu bằng cách sử dụng phương pháp mới để render trang web.

1. Lộ trình học React

1.1 Học React Class

  1. Cài đặt môi trường
  2. Lưu ý: những chữ viết hoa. Mình từng viết React.Component chữ component mình không viết hoa và nó không chạy


//Để chạy dùng lệnh
npm start

2. Tìm hiểu về JSX

JSX khá giống với HTML


{1+1} // kết quả bằng 2
{/**/}  // để ghi chú

3. Tìm hiểu về các components trong React

State chứa biến trong component, hàm map như vòng lặp duyệt qua mảng


import React from 'react';
​
class App extends React.Component {
   constructor() {
      super();
      this.state = {
         data: 
         [
            {
               "id":1,
               "name":"Foo",
               "age":"20"
            },
            {
               "id":2,
               "name":"Bar",
               "age":"30"
            },
            {
               "id":3,
               "name":"Baz",
               "age":"40"
            }
         ]
      }
   }
   render() {
      return (
         <div>
            <Header/>
            <table>
               <tbody>
                  {this.state.data.map((person, i) => <TableRow key = {i} 
                     data = {person} />)}
               </tbody>
            </table>
         </div>
      );
   }
}
class Header extends React.Component {
   render() {
      return (
         <div>
            <h1>Header</h1>
         </div>
      );
   }
}
class TableRow extends React.Component {
   render() {
      return (
         <tr>
            <td>{this.props.data.id}</td>
            <td>{this.props.data.name}</td>
            <td>{this.props.data.age}</td>
         </tr>
      );
   }
}
export default App;

4. State

5. Props

Props để truyền biến từ component này sang component khác

6. component API

  • setState => cập nhật state và khi state cập nhật sẽ render lại
  • forUpdate => cập nhật và render lại
  • findDOMNode => lấy element chỉnh sửa


import React from 'react';
import ReactDOM from 'react-dom';
​
class App extends React.Component {
   constructor() {
      super();
      this.findDomNodeHandler = this.findDomNodeHandler.bind(this);
   };
   findDomNodeHandler() {
      var myDiv = document.getElementById('myDiv');
      ReactDOM.findDOMNode(myDiv).style.color = 'green';
   }
   render() {
      return (
         <div>
            <button onClick = {this.findDomNodeHandler}>FIND DOME NODE</button>
            <div id = "myDiv">NODE</div>
         </div>
      );
   }
}
export default App;

7. Vòng đời component

8. Form trong React

9. Event trong React

10. Refs

Refs giống như id trong HTML để lấy element nào đó làm những việc nào đó.


import React from 'react';
import ReactDOM from 'react-dom';
​
class App extends React.Component {
   constructor(props) {
      super(props);
  
      this.state = {
         data: ''
      }
      this.updateState = this.updateState.bind(this);
      this.clearInput = this.clearInput.bind(this);
   };
   updateState(e) {
      this.setState({data: e.target.value});
   }
   clearInput() {
      this.setState({data: ''});
      ReactDOM.findDOMNode(this.refs.myInput).focus();
   }
   render() {
      return (
         <div>
            <input value = {this.state.data} onChange = {this.updateState} 
               ref = "myInput"></input>
            <button onClick = {this.clearInput}>CLEAR</button>
            <h4>{this.state.data}</h4>
         </div>
      );
   }
}
export default App;

11. Key trong React

Key là định danh để chỉnh sửa element nhanh hơn

1.2 Học React Function sử dụng HOOK

  1. React Hook là gì và lợi ích
  2. Tìm hiểu các hook trong react
  3. useState trong React Hooks


import React, { useState } from 'react';
​
function Example() {
  // Khai báo một biến state mới, gọi nó là "count"
  const [count, setCount] = useState(0);
  
  return (
  );
}

1.3 Material UI

Trang web material ui

2 thư viện design khác:


import React, {useState, useEffect} from 'react';
import { makeStyles } from '@material-ui/core/styles';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
import TableContainer from '@material-ui/core/TableContainer';
import TableHead from '@material-ui/core/TableHead';
import TableRow from '@material-ui/core/TableRow';
import Paper from '@material-ui/core/Paper';
​
​
​
function createData(id, name, age) {
  return { id, name, age };
}
​
​
​
export default function DenseTable() {
  const [rows, setRow] = useState([
                            createData(1, 'Foo', 10),
                            createData(1, 'Bar', 20),
                            createData(1, 'Baz', 30)
                        ]);
​
 
  //const classes = useStyles();
​
  const classes = {
    table: "minWidth: 650",
    w_300: {
      width: 300,
    },
    w_50: {
      width: 50,
    }
  };
​
​
​
  return (
      <TableContainer component={Paper} style={classes.w_300}>
        <Table className={classes.table} size="small" aria-label="a dense table">
          <TableHead>
            <TableRow>
              <TableCell style={classes.w_50}>id</TableCell>
              <TableCell align="left">Name</TableCell>
              <TableCell align="right">Age</TableCell>
            </TableRow>
          </TableHead>
          <TableBody>
            {rows.map((row) => (
              <TableRow key={row.name}>
                <TableCell component="th" scope="row">
                  {row.id}
                </TableCell>
                <TableCell align="left">{row.name}</TableCell>
                <TableCell align="right">{row.age}</TableCell>
              </TableRow>
            ))}
          </TableBody>
        </Table>
      </TableContainer>
  );
}

1.4 Kết hợp Laravel với React

Đến đây mình khá bất ngờ, khi 2 công nghệ có thể kết hợp với nhau. Laravel làm backend và React làm frontend. Và mình biết được 2 công nghệ kết hợp với nhau thông qua API.

Đầu tiên các bạn cần tạo API bên Laravel theo bài viết này: https://code.tutsplus.com

Sau khi có API bên project React các bạn sử dụng fetch hoặc axios để lấy dữ liệu. https://reactjs.org/docs/faq-ajax.html

Lưu ý: khi bạn có dữ liệu và đổ lên state để sử dụng sẽ có một vòng lặp vô tận cứ chạy mãi, cứ gọi request phát sinh lỗi 429. Do đó bạn cần thêm tham số thứ 2 vào hàm useEffect() một mảng rỗng, để chỉ gọi request lần đầu tiên. Bạn tham khảo thêm bài viết https://topdev.vn


//code phía react
import React, {useState, useEffect} from 'react';
import { makeStyles } from '@material-ui/core/styles';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
import TableContainer from '@material-ui/core/TableContainer';
import TableHead from '@material-ui/core/TableHead';
import TableRow from '@material-ui/core/TableRow';
import Paper from '@material-ui/core/Paper';
​
​
​
// function createData(id, name, age) {
//   return { id, name, age };
// }
​
​
​
export default function DenseTable() {
  const [rows, setRow] = useState([]);
​
  useEffect( () => {
    fetch("http://localhost/two_cn/server/api/data")
      .then(
          function(response) {
            if (response.status !== 200) {
              console.log('Lỗi, mã lỗi ' + response.status);
              return;
            }
            // parse response data
            response.json().then(data => {
              //console.log(data);
              setRow(data);
            })
          }
        )
        .catch(err => {
          console.log('Error :-S', err)
        });
  }, []);
​
  //const classes = useStyles();
​
  const classes = {
    table: "minWidth: 650",
    w_300: {
      width: 300,
    },
    w_50: {
      width: 50,
    }
  };
​
​
​
  
​
  return (
      <TableContainer component={Paper} style={classes.w_300}>
        <Table className={classes.table} size="small" aria-label="a dense table">
          <TableHead>
            <TableRow>
              <TableCell style={classes.w_50}>id</TableCell>
              <TableCell align="left">Name</TableCell>
              <TableCell align="right">Age</TableCell>
            </TableRow>
          </TableHead>
          <TableBody>
            {rows.map((row) => (
              <TableRow key={row.name}>
                <TableCell component="th" scope="row">
                  {row.id}
                </TableCell>
                <TableCell align="left">{row.name}</TableCell>
                <TableCell align="right">{row.age}</TableCell>
              </TableRow>
            ))}
          </TableBody>
        </Table>
      </TableContainer>
  );
}

1.5 Validate form bằng redux-form và kết hợp react với redux

Bạn cần đọc các bải viết này để hiểu rõ nó:

  • Redux là gì? Hiểu rõ cơ bản cách dùng Redux
  • Cơ bản về redux-form:
  • Bài viết này cho bạn cái nhìn đơn giản nhưng khá rõ về redux-form. Đồng thời có một ví dụ đơn giản theo từng quy trình tạo redux-form: Tạo Store => Tạo Form => Tạo Component <Field/> => Gọi form và xử lý submit
  • Bắt đầu với Redux? Tại sao Redux:
  • Bài viết này là cái nhìn tổng quan và chi tiết về redux. Bao gồm tạo store, action, reducer. Bên cạnh đó là code ví dụ cho từng phần.
  • Redux Form Validate Tutorial:
  • Đây là bài viết hướng dẫn tạo một redux-form hướng dẫn từ a-z. Bao gồm cả validate. Cũng khá là dễ hiểu và đơn giản.
  • Ví dụ đơn giản với react và redux tại phía client 
  • Bài viết lại là một project làm bằng react và redux đơn giản. Nó là một quy trình của redux. Tạo các state dùng chung => Tạo reducer => tạo store với reducer => Truyền store => Sử dụng store tại component => component gọi action reducer

2. Kinh nghiệm

2.1 return

Return nhiều element chúng ta cần bọc nó với 1 container (div, …)

2.2. if else

Không thể sử dụng If Else trong JSX, thay vào đó sử dụng điều kiện rút gọn


import React from 'react';
​
class App extends React.Component {
   render() {
      var i = 1;
      return (
         <div>
            <h1>{i == 1 ? 'True!' : 'False'}</h1>
         </div>
      );
   }
}
export default App;

2.3 style

Bỏ vào một biến để sử dụng


import React from 'react';
​
class App extends React.Component {
   render() {
      var myStyle = {
         fontSize: 100,
         color: '#FF0000'
      }
      return (
         <div>
            <h1 style = {myStyle}>Header</h1>
         </div>
      );
   }
}
export default App;

2.4 Thực hiện hành động nào đó sau khi gọi API


const add_person = (person) => {
  console.log(person);
  axios.post('http://localhost/two_cn/server/api/data', person)
    .then(res => {
      console.log(res.data);
    });
}
 get_data();

Bạn đang bực bội mình viết gọi API rồi tới hàm mà sao nó cứ chạy hàm dưới trước rồi gọi API sau. Để giải quyết bạn phải sử dụng promise. Lúc này nó mới hiểu là gọi API xong mới xử lý.


const add_person = (person) => {
  console.log(person);
  axios.post('http://localhost/two_cn/server/api/data', person)
    .then(res => {
      console.log(res.data);
      get_data();
    });
}
// hàm get_data() sẽ thực hiện khi gọi API xong

Tham khảo thêm: Làm việc với asynchronous APIs

2.5 Dấu …

Ký tự gì lạ vậy trời. Đó là câu hỏi lúc đầu tôi mới bắt đầu học react. Thật ra nó chỉ là tách phần tử ra giúp mình thôi. (đúng hơn là copy mảng hoặc đối tượng)

Ví dụ bạn có mảng a = [5,6,7,8,9]

b = […a, 7]  => b = [5,6,7,8,9,7]

Tương tự với object.

Bạn tham khảo thêm tại 2 bài viết này nhé!

2.6 Redux

Tạo defaultState


const defaultValue = {
    listUser: [],
}
const defaultState = defaultValue;

Tạo reducer


function reducerUser(state = defaultState, action) {
    switch (action.type) {
      case 'ADD':
        add_person(action.formData);
        return state;
​
       case 'SET_DATA':
        let users_data=[...action.data];
        return {...state, listUser:users_data};
​
        default:
        return state;
    }
    //return state;
}

Đăng ký rootReducer


const rootReducer = combineReducers({
    reducerUser,
    form: formReducer
});

Tạo store


const store = createStore(rootReducer);

Tạo provider


ReactDOM.render(
    <Provider store={store}>
      <Router history={hist}>
        <Switch>
          <Route path="/admin" component={Admin} />
          <Route path="/rtl" component={RTL} />
          <Redirect from="/" to="/admin/dashboard" />
        </Switch>
      </Router>
    </Provider>,
  document.getElementById("root")
);
​

Sử dụng


function TableList = (props) {
    const {listUser} = props; 
}
​
​
export default connect (function(state) {
  console.log("FROM VIEWER:", state); // in giá trị state
  return {
    listUser: state.reducerUser.listUser
  };
})(TableList);

Gọi action đến store


const add_person = (person) => {
  props.dispatch({
    type: 'ADD',
    formData: person
    });
}

2.7 Redux-form

2.7.1 Quy trình

Tạo store cho redux-form


const rootReducer = combineReducers({
    form: formReducer
});
​
const store = createStore(rootReducer);

Tạo form, Field của redux-form


const renderField = (props) => {
    const { input, label, type, meta: { touched, error, warning } } = props;
    return (
    <div>
      <label className="control-label">{label}</label>
      <div>
        <input {...input} placeholder={label} type={type} className="form-control"/>
        {touched && ((error && <span className="text-danger">{error}</span>) || (warning && <span>{warning}</span>))}
      </div>
    </div>
    );
}
​
let Create = (props) => {
    /*--props--*/
    const { handleSubmit, pristine, reset, submitting } = props;
​
    /*--Variable--*/
​
    /*--state--*/
    const dataForm = props.dataForm;
​
    /*--method--*/
    const cancel = props.cancel;
​
    /**--style--*/
    
    return(
        <div>
            <form  onSubmit={ handleSubmit }>
              <div className="form-group">
                <Field name="name" component={renderField} label="Name"/>
              </div>
              <div className="form-group">
                <Field name="age" component={renderField} label="Age"/>
              </div>
              <br />
              <br />
              <div className="form-group">
                <button type="submit" className="btn btn-primary">Submit</button>
                <Button style={{marginLeft: '10px'}} variant="contained" color="secondary" onClick={cancel}>
                  Hủy
                </Button>
              </div>
              
            </form>
        </div>
    );
}

tạo redux-form:

  • Tên form là duy nhất nhé


Create =  reduxForm({
  form: 'create', // a unique identifier for this form
  validate
})(Create)

Validate


const validate = values => {
    const errors = {}
    if (!values.name) {
      errors.name = 'Không được để trống'
    } else if (values.name.length < 2) {
      errors.name = 'Nhập ít nhất 2 ký tự'
    }
    if (!values.age) {
      errors.age = 'Không được để trống'
    } 
    
    return errors
  }

Gọi ra xài thôi


import React, {useState, useEffect, useCallback, updateState} from "react";
import ............
​
function LayoutCRUD(props) {
​
  const handleSubmitFormCreate = (person) => {
        .........
  }
​
  return (              
    <Create 
         onSubmit={handleSubmitFormCreate} 
    />
  );
}

(*) Create truyền props onSubmit là props mặc định của redux-form cho phương thức submit. Ở Form tạo của redux-form sẽ sử dụng props này với tên là handleSubmit. Bạn lưu ý kỹ chỗ này nhé.

2.7.2 Set giá trị cho form

Tình huống:

  • Bạn nhấn nút thêm mới và muốn các trường trở về giá trị rỗng
  • Bạn nhấn nút chỉnh sửa và muốn đổ giá trị đó vào cho form

Khi mới tìm hiểu, mình bị bí chỗ này, mất cả tuần tìm hiểu không ra. Cảm thấy khá nãn, nhưng cuối cùng cũng thành công. Bạn làm theo bên dưới nhé.

Cách 1:

Ở file gọi redux-form của bạn bạn sẽ truyền props data qua cho component chứa redux-form


import React, {useState, useEffect, useCallback, updateState} from "react";
import ............
​
function LayoutCRUD(props) {
  const [dataForm, setDataForm] = useState(personEmpty);
  
  const handleSubmitFormCreate = (person) => {
        .........
  }
​
  return (              
    <Create 
        onSubmit={handleSubmitFormCreate} 
        dataForm={dataForm}
    />
  );
}

component chứa redux-form, bạn nhận giá trị từ props này


let Create = (props) => {
    /*--props--*/
    const { handleSubmit, pristine, reset, submitting } = props;
​
    /*--state--*/
    const dataForm = props.dataForm;
​
    
    return(
        <div>
            <form  onSubmit={ handleSubmit }>
                <Field name="name" component={renderField} label="Name"/>
                <Field name="age" component={renderField} label="Age"/>
                <button type="submit" className="btn btn-primary">Submit</button>
            </form>
        </div>
    );
}

Có data cho component rồi thì, set giá trị bằng cách viết vào function connect

  • Gồm connect và hàm mapStateToProps


let Create = (props) => {
    /*--props--*/
    const { handleSubmit, pristine, reset, submitting } = props;
​
    /*--state--*/
    const dataForm = props.dataForm;
​
    
    return(
        <div>
            <form  onSubmit={ handleSubmit }>
                <Field name="name" component={renderField} label="Name"/>
                <Field name="age" component={renderField} label="Age"/>
                <button type="submit" className="btn btn-primary">Submit</button>
            </form>
        </div>
    );
}
​
const mapStateToProps = (state, props) => ({
  initialValues: props.dataForm, // retrieve name from redux store 
})
​
export default connect(
  mapStateToProps
)(reduxForm({
   form: 'simple', // a unique identifier for this form
  enableReinitialize: true
})(Create))
Cách 2:
  • add props initialized, initialize, change
  • Sử dụng trong useEffect nếu đã khỏi tạo => thay đổi giá trị ngược lại thì khởi tạo giá trị.


let TableCustom = (props) => {
​
  /*--props--*/
  const { handleSubmit, pristine, reset, submitting } = props;
​
  const {initialized, initialize, change} = props;
​
  const editRow = props.editRow;
  
  useEffect( () => { 
              if(initialized) {
                change("name", editRow.name); 
                change("age", editRow.age); 
              } else {
                initialize({...editRow});
              }
             }, 
         []);
  
    return (
      ...
    );
}
Cách 3: (giống cách 2 nhưng chỉ sử dụng initialize)


useEffect( () => { 
                initialize({...editRow});
             }, 
         []);

2.7.3 Sử dụng props của redux-form

Bạn sử dụng như ở mục 2.7.3 Cần xài cái nào thì bỏ vô như thế này:


const { handleSubmit, pristine, reset, submitting } = props;
​
const {initialized, initialize, change} = props;

Trang chủ redux-form có các props mà redux-form cung cấp: https://redux-form.com/

2.7.4 Thẻ form và handleSubmit

Thẻ form phải:

  • Bao cả Field và nút submit


<form  onSubmit={ handleSubmit }>
  <Field name="name" component={renderField} label="Name"/>
  <Field name="age" component={renderField} label="Age"/>
  <button type="submit" className="btn btn-primary">Submit</button>
</form>

Hoặc:

  • Chỉ bao quanh Field
  • button sẽ bắt sự kiện onClick và truyền vào props handleSubmit của redux-form


<form>
  <Field name="name" component={renderField} label="Name"/>
  <Field name="age" component={renderField} label="Age"/>
</form>
  
<button type="submit" className="btn btn-primary" onSubmit={handleSubmit}>Submit</button>

2.8 Việc sử dụng, copy mảng, đối tượng

Bạn nên sử dụng kí tự “…” để truyền mảng hoặc đối tượng vào (copy thành mảng, đối tượng khác), để tránh phát sinh lỗi. 


initialize({...editRow});
​
//thay vì:
initialize(editRow);

2.9 Lỗi thiếu thư viện

Kiểm tra file package.json có thư viện đó chưa? Nếu chưa:

  • tiến hành install
  • cài xong => npm start lại

2.10 Plugin cho react

2.10.1 Cách tìm kiếm google

Bạn search như sau:

  • vấn đề cần tìm + npm

Hoặc

  • vấn đề cần tìm + react

Tiêu chí chọn plugin:

  • Dễ cài đặt
  • Nhiều option để dễ tùy biến
  • Lượt tải nhiều

2.10.2 Các plugin react tổng hợp

  • Loading Data: react-spinner
  • Đọc HTML: react-html-parser (code html => render ra , thường là CKEditor lưu dạng HTML)
  • Editor: 
  • Tag tab:

3. IDE Webstome

3.1 Đi đến nhanh một component con

ctrl + click

3.2 Phát hiện các biến không sử dụng

Sử dụng plugin … cho webstome IDE. 

4. Theme Material

4.1 Material Dashboard React

Link trang chủ theme: https://www.creative-tim.com

4.1.1 Xóa sidebar lựa chọn theme

Bạn vào Views/Layout/Admin.js

Tìm state FixedClass và bỏ chữ “show” đi.

Nếu muốn bỏ luôn bánh răng:

Bỏ luôn component <FixedPlugin> ở gần cuối file

5. Deploy lên server

Bạn chạy lệnh:


npm run build

Sau khi chạy xong,bạn vào thư mục public sẽ có 2 file dạng …app.js và …app.css

Bạn upload 2 file đó lên server là có thể chạy được.

Trường hợp có hình ảnh:

  • public/images: tất cả hình ảnh sẽ nằm trong đây
  • Bạn cũng upload folder này lên luôn nha.


Copyright © 2025 All Right Reserved