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.
//Để chạy dùng lệnh npm start
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
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;
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;
Key là định danh để chỉnh sửa element nhanh hơn
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 ( ); }
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> ); }
Đế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> ); }
Bạn cần đọc các bải viết này để hiểu rõ nó:
Return nhiều element chúng ta cần bọc nó với 1 container (div, …)
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;
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;
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
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é!
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 }); }
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:
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é.
Tình huống:
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é.
Ở 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
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))
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 ( ... ); }
useEffect( () => { initialize({...editRow}); }, []);
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/
Thẻ form phải:
<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:
<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>
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);
Kiểm tra file package.json có thư viện đó chưa? Nếu chưa:
Bạn search như sau:
Hoặc
Tiêu chí chọn plugin:
ctrl + click
Sử dụng plugin … cho webstome IDE.
Link trang chủ theme: https://www.creative-tim.com
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
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: