Hooks
Hook就是“钩子”的意思。在React中,Hooks就是把某个目标结果钩到某个可能会变化的数据源或者事件源上,那么当被钩到的数据或者事件发生变化时,产生这个目标结果的代码会重新执行,产生更新后的结果。
常用钩子
- React.useState()
- React.useEffect()
- React.useRef()
- React.useReducer()
- React.useContext()
useState():状态钩子
useState()
用于为函数组件引入状态(state)。纯函数不能有状态,所以把状态放在钩子里面。
const [ XXX, setXXX ] = React.useState(initValue)
示例:
import React, { useState } from 'react';
const useStateDemo = () => {
const [count, setCount] = useState(1);
return (
<div>
{count}
<Button onClick={() => setCount(count+1)}>count+1</Button>
</div>
)
}
export default useStateDemo;
1为count的初始化值,count是一个变量,setCount是用来更新变量count值的一个函数,通常以set + 变量名命名。
useEffect():副作用钩子
useEffect()在函数组件中执行副作用操作,类似于组件中的生命周期钩子。
useEffect(() => { getXXX() }, [XXX]);
示例:
import React, { useState, useEffect } from 'react';
const useEffectDemo = () => {
const [count, setCount] = useState(1);
const getCount = () => {
console.log('count', count)
}
useEffect(() => {
getCount()
}, [count]);
return (
<div>
{count}
<Button onClick={() => setCount(count + 1)}>count+1</Button>
</div>
)
}
export default useEffectDemo;
第一个参数getCount是一个函数,通常将异步操作的代码放在里面。第二个参数是一个数组,用于给出 Effect 的依赖项,每当变量count的值发生变化时,useEffect()
就会执行。组件第一次渲染时,useEffect()
也会执行。
useRef(): 变量钩子
useRef 是一个对象,它拥有一个 current 属性,并且不管函数组件执行多少次,返回的对象永远都是原来那一个。useRef不仅可以用来管理 DOM ref 的,还可以相当于 this存放任何变量。useRef
和useState
不同,如果一个状态或者数据会影响DOM的渲染结果,一定要避免使用useRef来保持引用
const useRefDemo = () => {
const inputRef = useRef(null)
const refTest = useRef(0);
console.log('refTest', refTest)
const getInputValue=()=>{
console.log('inputValue:', inputRef.current.value);
}
const add = () => {
refTest.current += 1;
console.log('add', refTest);
};
return (
<div>
<div>
<input ref={inputRef} type='text'/>
<Button onClick={getInputValue}>点击获取input值</Button>
</div>
<div>
<h1>当前current:{refTest.current}</h1>
<Button onClick={add}>count+1</Button>
</div>
</div>
)
}
export default useRefDemo;
效果:
方法一:用来绑定文本框 ref ,通过内置的current属性得到input上输入的值。
方法二:第一次控制台打印出current,初始值为0,是定义时传入的0 ,点击count+1按钮后显示 current的值已经改变了,但是在页面上并没有更新,因为useRef 不像 useState 会主动通知页面重新渲染。
useReducer():action 钩子
useReducer()可以为函数式组件提供类似Redux的功能,类似于vuex都是提供状态管理的
const [state, dispatch] = useReducer(reducer, initialState);
import React, { useReducer } from 'react'
const reducer = (state, action) => {
if(action === 'add') {
return state+1
}
return state
}
const useReducerDemo = () => {
const [state, dispatch] = useReducer(reducer, 0)
return (
<div>
<h1>{state}</h1>
<Button onClick={() => dispatch('add')}>+1</Button>
</div>
)
}
export default useReducerDemo;
定义[state, dispatch]来接收我们后面的useReducer,因为useReducer包含两个参数,一个参数为useReducer的函数reducer,一个为初始值0
useContext():共享状态钩子
如果需要在组件之间共享状态,可以使用useContext()
。
import React, { useContext } from 'react';
const nameContext = React.createContext();
const useContextDemo = () => {
return (
<nameContext.Provider value={'派大星'}>
<div>
<ShowNameProvider />
<ShowNameConsumer />
</div>
</nameContext.Provider>
)
}
const ShowNameProvider = () => {
const name = useContext(nameContext);
return(
<div>
Provider: 我是 {name}
</div>
)
}
const ShowNameConsumer = () => {
return (
<div>
<nameContext.Consumer>
{name=> <div>Consumer: 我是 {name}</div>}
</nameContext.Consumer>
</div>
)
}
export default useContextDemo;
效果:
首先创建一个context,返回一个有两个值的对象{Provider , Consumer}
方法一:使用Provider 为子组件提供value,再调用useContext,传入从React.createContext获取的上下文对象
方法二:使用Consumer从上下文获取value
使用React搭建用户信息模块(纯前端)
- 新建Content.js,并在app.js引用
Content.js
import React, { useState, useEffect } from 'react';
import Filter from './Filter';
import TableDetail from './TableDetail';
const Content = () => {
const [filterData, setFilterData] = useState([])
const getFilterData = (e) => {
console.log(e)
setFilterData(e)
}
return (
<div>
<div style={{ paddingLeft: '10px' }}>
<h1>筛选数据</h1>
<Filter getFilterData={getFilterData} />
</div>
</div>
)
}
export default Content;
import React from 'react';
import Content from './components/Content'
export default function App() {
return (
<Content/>
);
}
- 新建Filter.js,在Content插入Filter组件,并将Filter组件的筛选信息传给Content
Filter.js
import React, { useState, useEffect } from 'react';
import { Divider, Button, Radio } from 'antd';
import { SearchOutlined } from '@ant-design/icons';
import '../common/style/filter.css'
const Filter = (props) => {
const { getFilterData } = props;
const [genderOptions, setGenderOptions] = useState(false);
const [roleOptions, setRoleOptions] = useState(false);
const [role, setRole] = useState('admin');
const [gender, setGender] = useState('女');
// 筛选项
const getGenderOptions = () => {
let option = [
{ label: '男', value: '男' },
{ label: '女', value: '女' },
]
setGenderOptions(option);
};
const getRoleOptions = () => {
let option = [
{ label: 'admin', value: 'admin' },
{ label: 'user', value: 'user' },
]
setRoleOptions(option);
};
useEffect(() => {
getGenderOptions()
getRoleOptions ()
}, []);
const onGender = (e) => {
setGender(e.target.value)
}
const onRole = (e) => {
setRole(e.target.value)
}
const search = () => {
let query = {
role,
gender,
}
console.log('筛选:', query)
getFilterData(query)
}
return (
<div>
<span className='tags'>性别</span>
<Radio.Group
options={genderOptions}
onChange={onGender}
value={gender}
/>
<Divider type="vertical" />
<span className='tags'>角色</span>
<Radio.Group
options={roleOptions}
onChange={onRole}
value={role}
/>
<Divider type="vertical" />
<Button
type="primary"
shape="round"
icon={< SearchOutlined />}
size='middle'
onClick={() => search()}
>
查询
</Button>
<Divider></Divider>
</div>
)
}
export default Filter;
效果:
- 新建 TableDetail.js,在Content插入 TableDetail组件,在Content筛选整理数据后将tableData的数据传递给Table组件
Content.js
import React, { useState, useEffect } from 'react';
import Filter from './Filter';
import TableDetail from './TableDetail';
const Content = () => {
const [tableData, setTableData] = useState([])
const [filterData, setFilterData] = useState([])
const getTableData = () => {
const data = [{
key: '1',
name: '派大星',
age: 18,
account: '123456789',
password: '123456',
gender: '女',
tel: '12345678900',
address: '广州市番禺区',
role: 'user',
tags: ['一级', '二级'],
},{
key: '2',
name: '海绵宝宝',
age: 22,
account: '123456780',
password: '123456',
gender: '男',
tel: '12345678900',
address: '广州市番禺区',
role: 'admin',
tags: ['一级'],
},{
key: '3',
name: '章鱼哥',
age: 20,
account: '123456781',
password: '123456',
gender: '男',
tel: '12345678900',
address: '重庆市渝北区',
role: 'admin',
tags: ['一级'],
},{
key: '4',
name: '蟹老板',
account: '123456782',
password: '123456',
age: 19,
gender: '男',
tel: '12345678900',
address: '南宁市江南区',
role: 'user',
tags: ['二级'],
},{
key: '5',
name: '珊迪',
account: '123456783',
password: '123456',
age: 19,
gender: '女',
tel: '12345678900',
address: '重庆市渝北区',
role: 'admin',
tags: ['一级', '二级'],
}]
let table = []
data.map((item) => {
if (item.role === filterData.role && item.gender === filterData.gender) {
table.push(item)
}
})
setTableData(table)
}
const getFilterData = (e) => {
console.log(e)
setFilterData(e)
}
useEffect(() => {
getTableData()
}, [filterData]);
return (
<div>
<div style={{ paddingLeft: '10px' }}>
<h1>筛选数据</h1>
<Filter getFilterData={getFilterData} />
<TableDetail tableData={tableData}/>
</div>
</div>
)
}
export default Content;
TableDetail.js
import React, { useState, useEffect } from 'react';
import { Table, Tag, Space, Button, message, Empty } from 'antd';
const { Column } = Table;
const TableDetail = (props) => {
const {tableData} = props
const renderUserMessage = () => {
if (tableData.length > 0) {
return (
<span>
<h1>用户表格</h1>
<Table dataSource={tableData}>
<Column
title="姓名"
dataIndex="name"
key="name"
width={150}
/>
<Column
title="角色"
dataIndex="role"
key="role"
/>
<Column
title="年龄"
dataIndex="age"
key="age"
width={150}
/>
<Column
title="账号"
dataIndex="account"
key="account"
width={150}
/>
<Column
title="密码"
dataIndex="password"
key="password"
width={200}
/>
<Column
title="性别"
dataIndex="gender"
key="gender"
/>
<Column
title="联系电话"
dataIndex="tel"
key="tel"
width={180}
/>
<Column
title="地址"
dataIndex="address"
key="address"
width={300}
/>
<Column
title="标签"
dataIndex="tags"
key="tags"
width={200}
render={ tags => (
<>
{tags.map(tag => {
let color = tag === '一级' ? 'geekblue' : 'green';
return (
<Tag color={color} key={tag}>
{tag}
</Tag>
);
})}
</>
)}
/>
<Column
title="操作"
key="action"
render={(text, record) => (
<Space size="middle">
<Button
type="primary"
size='mini'
onClick={() => actionRow(record.key, 'edit')}>
编辑
</Button>
<Button
type="primary"
size='mini'
danger
onClick={() => actionRow(record.key, 'delete')}>
删除
</Button>
</Space>
)}
/>
</Table>
</span>
);
} else {
return (
<Empty />
);
}
}
const actionRow = (e, type) => {
if (type === 'edit') {
console.log('edit——id', e)
} else if (type === 'delete') {
console.log('delete——id', e)
}
}
return (
<div>
{renderUserMessage()}
</div>
)
}
export default TableDetail;
效果
- 新增用户,新建CreateModal.js,并在TableDetail.js插入CreateModal.js,通过showCreateModel控制CreateModal是否弹出。
TableDetail.js
import CreateModal from './comp/CreateModal';
const [showCreateModel, setShowCreateModel] = useState(false)
const createModel = () => {
return (
<div>
<CreateModal showCreateModel={showCreateModel} changeModal={isShowCreateModel}/>
</div>
)
}
const isShowCreateModel = (e) => {
setShowCreateModel(e)
}
CreateModal.js
import React from 'react';
import { Modal, Form, Input, InputNumber, Button, Radio, Checkbox, Row, Col } from 'antd';
const CreateModal = (props) => {
const { showCreateModel, changeModal } = props;
const [form] = Form.useForm();
const layout = {
labelCol: {
span: 6,
},
wrapperCol: {
span: 14,
},
};
const validateMessages = {
required: '${label} 不能为空!',
types: {
number: '${label} is not a valid number!',
},
number: {
range: '${label} 为 ${min}~${max}',
},
};
const onFinish = (values) => {
console.log(values);
changeModal(false);
form.resetFields();
};
const hideModal = (e) => {
form.resetFields();
changeModal(e)
}
return (
<div>
<Modal
title="Modal"
forceRender
visible={showCreateModel}
onCancel={() => hideModal(false)}
footer={[]}
>
<Form
{...layout}
form={form}
name="nest-messages"
onFinish={onFinish}
validateMessages={validateMessages}>
<Form.Item
name={['user', 'name']}
label="姓名"
rules={[
{
required: true,
},
]}
>
<Input />
</Form.Item>
<Form.Item
name={['user', 'tel']}
label="联系电话"
rules={[
{
required: true,
},
]}
>
<Input />
</Form.Item>
<Form.Item
name={['user', 'age']}
label="年龄"
rules={[
{
type: 'number',
min: 0,
max: 99,
},
]}
>
<InputNumber />
</Form.Item>
<Form.Item name={['user', 'gender']} label="性别">
<Radio.Group>
<Radio value="男">男</Radio>
<Radio value="女">女</Radio>
</Radio.Group>
</Form.Item>
<Form.Item name={['user', 'role']} label="角色">
<Radio.Group>
<Radio value="user">user</Radio>
<Radio value="admin">admin</Radio>
</Radio.Group>
</Form.Item>
<Form.Item name={['user', 'tags']} label="标签">
<Checkbox.Group>
<Row>
<Col span={12}>
<Checkbox
value="一级"
style={{ lineHeight: '32px' }}>
一级
</Checkbox>
</Col>
<Col span={12}>
<Checkbox
value="二级"
style={{ lineHeight: '32px' }}>
二级
</Checkbox>
</Col>
</Row>
</Checkbox.Group>
</Form.Item>
<Form.Item wrapperCol={{ ...layout.wrapperCol, offset: 5 }}>
<Button
onClick={() => hideModal(false)}
size='large'
style={{
width: '48%',
marginRight: '4%'
}}>
取消
</Button>
<Button
type="primary"
htmlType="submit"
size='large'
style={{ width: '48%' }}>
提交
</Button>
</Form.Item>
</Form>
</Modal>
</div>
)
}
export default CreateModal;
效果
优化表格样式
TableDetail.css
.ant-table-thead > tr >th{
color: white !important;
background: #4371aa !important;
font-weight: bolder !important;
text-align: center !important;
}
.ant-table-thead > tr > th:first-child{
border-radius: 30px 0px 0px 30px !important;
}
.ant-table-thead > tr > th:last-child{
border-radius: 0px 30px 30px 0px !important;
}
.ant-table-tbody > tr > td {
color: #4371aa !important;
background: #3070b91c !important;
font-weight: bold !important;
text-align: center !important;
}
.ant-table-tbody > tr:hover{
background: #1b518625 !important;
}
.ant-table-tbody > tr > td:first-child{
border-radius: 30px 0px 0px 30px !important;
}
.ant-table-tbody > tr > td:last-child{
border-radius: 0px 30px 30px 0px !important;
}