Hook
是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
State Hook
useState 唯一的参数就是初始 state。
可以声明多个 state 变量
Effect Hook
useEffect 是一个 Effect Hook,给函数组件增加了操作副作用的能力。
副作用函数还可以通过返回一个函数来指定如何“清除”副作用。
useState 一样,你可以在组件中多次使用 useEffect
自定义 Hook
在两个组件中使用它:
Hook 使用规则
- 只能在函数最外层调用 Hook。不要在循环、条件判断或者子函数中调用。
- 只能在 React 的函数组件中调用 Hook。不要在其他 JavaScript 函数中调用
初识Context
Context 通过组件树提供了一个传递数据的方法,从而避免了在每一个层级手动的传递 props 属性。context可以实现跨层级进行数据传递。
context api 给出三个概念:
React.createContext()、Provider、Consumer;
- React.createContext()
这个方法用来创建context对象,并包含Provider、Consumer两个组件<Provider />、<Consumer />
, defaultValue可以设置共享的默认数据
const {Provider, Consumer} = React.createContext(defaultValue);
- Provider
数据的生产者,用来传递给子组件或后代组件。value:放置共享的数据。
<Provider
value={/*共享的数据*/}>
/*里面可以渲染对应的内容*/
</Provider>
- Consumer
数据的消费者,通过订阅Provider传入的context的值,来实时更新当前组件的状态。
<Consumer>
{value => /*根据上下文 进行渲染相应内容*/}
</Consumer>
怎么样使用Context?
Context的使用主要分为创建、插入、访问三个流程,通过创建context对象提供共享组件,将Provider指定嵌套至需要使用共享数据的顶层结构,然后各后代组件通过context访问共享数据(变量、常量、方法等)。
创建一个context
// 创建文件LoginContext.js
import { createContext } from 'react';
const LoginContext = createContext({});
export default LoginContext;
Provider包装一个组件
//创建一个LoginFrom.js
import { Tabs, Form } from 'antd';
import React, { useState } from 'react';
import useMergeValue from 'use-merge-value';
import classNames from 'classnames';
import LoginContext from './LoginContext';
import LoginItem from './LoginItem';
import LoginSubmit from './LoginSubmit';
import LoginTab from './LoginTab';
import styles from './index.less';
const Login = props => {
const { className } = props;
const [tabs, setTabs] = useState([]);
const [active, setActive] = useState();
const [type, setType] = useMergeValue('', {
value: props.activeKey,
onChange: props.onTabChange,
});
const TabChildren = [];
const otherChildren = [];
React.Children.forEach(props.children, child => {
if (!child) {
return;
}
if (child.type.typeName === 'LoginTab') {
TabChildren.push(child);
} else {
otherChildren.push(child);
}
});
return (
<LoginContext.Provider
value={{
tabUtil: {
addTab: id => {
setTabs([...tabs, id]);
},
removeTab: id => {
setTabs(tabs.filter(currentId => currentId !== id));
},
},
updateActive: activeItem => {
if (active[type]) {
active[type].push(activeItem);
} else {
active[type] = [activeItem];
}
setActive(active);
},
}}
>
<div className={classNames(className, styles.login)}>
<Form
form={props.from}
onFinish={values => {
if (props.onSubmit) {
props.onSubmit(values);
}
}}
>
{tabs.length ? (
<React.Fragment>
<Tabs
animated={false}
className={styles.tabs}
activeKey={type}
onChange={activeKey => {
setType(activeKey);
}}
>
{TabChildren}
</Tabs>
{otherChildren}
</React.Fragment>
) : (
props.children
)}
</Form>
</div>
</LoginContext.Provider>
);
};
Login.Tab = LoginTab;
Login.Submit = LoginSubmit;
Login.UserName = LoginItem.UserName;
Login.Password = LoginItem.Password;
Login.Mobile = LoginItem.Mobile;
Login.Captcha = LoginItem.Captcha;
export default Login;
调用共享数据
//创建文件LoginTab.js
import React, { useEffect } from 'react';
import { Tabs } from 'antd';
import LoginContext from './LoginContext'; // 创建的共享context
const { TabPane } = Tabs;
const generateId = (() => {
let i = 0;
return (prefix = '') => {
i += 1;
return `${prefix}${i}`;
};
})();
const LoginTab = props => {
useEffect(() => {
const uniqueId = generateId('login-tab-');
const { tabUtil } = props;
if (tabUtil) {
tabUtil.addTab(uniqueId);
}
}, []);
const { children } = props;
return <TabPane {...props}>{props.active && children}</TabPane>;
};
const WrapContext = props => (
<LoginContext.Consumer>
{value => <LoginTab tabUtil={value.tabUtil} {...props} />}
</LoginContext.Consumer>
); // 标志位 用来判断是不是自定义组件
WrapContext.typeName = 'LoginTab';
export default WrapContext;