此篇文章仅是对hooks入门的总结,老鸟略过吧~

React从16.8.X以后增加了一个新特性,react hooks 让我们看看这个新特性又带来了哪些惊喜呢~以下内容我们采取不同方式创建组件来进行对比总结

组件的创建方式:

用过react的都了解,传统react创建组件提供了两种方式,函数式与类(class)

class创建无状态组件

class App extends React.Component {
constructor(props) {
super(props);
} render() {
return <div>
<p>{this.props.name}</p>
</div>
}
} function renderApp() {
let appProps = {
name: 'dqhan'
}
ReactDOM.render(
<App {...appProps} />,
document.getElementById('app')
)
} renderApp();

添加状态管理

class App extends React.Component {
constructor(props) {
super(props);
this.state = {
name: props.name
};
this.handleChangeName = this.handleChangeName.bind(this);
} handleChangeName() {
this.setState({
name: '我变了'
})
} render() {
return (
<React.Fragment>
<p> {`hello~${this.state.name}`} </p>
<button onClick={this.handleChangeName}></button>
</React.Fragment>
);
}
}

我们通过class可以实现无状态组件以及常规组件通过setState的状态管理。

函数式创建组件

function App(props) {
return <p>{`hello! ${props.name}`}</p>
} function renderApp() {
let appProps = { name: "dqhan" };
ReactDOM.render(<App {...appProps} />, document.getElementById("app"));
}

函数式创建组件通常是无状态组件,这种方式没有办法在内部对状态统一管理,如果我们非要添加状态管理呢,那就只能借助redux啦~或者我们自己利用观察者模式实现一个发布订阅(emmmm比较勉强吧,毕竟实际开发中我们不可能这么做)

那么如果我们非要这么做呢,正题来了,React版本在16.8.X以后增添了一个新特性就是hooks。

hooks涉及API有useState、 useEffect、 useCallback、 useRef、 useMemo、 React.memo、 useReducer等,具体可以参考官方文档,我们来看一下hooks怎么用

React Hooks创建组件

无状态组件

function App(props) {
return <div>
<p>{`hello~${props.name}`}</p>
</div>
}

哈哈,跟函数式一样,毕竟我们要在函数式组件里添加状态管理嘛

1.useState

作用:添加状态管理

function App() {
let [name, setName] = useState('dqhan');
return <div>
<p>{`hello~${name}`}</p>
<button onClick={() => setName('我变了')}>Click</button>
</div>;
}

react hook可以管理自己的状态,有自己的函数钩子,这点相比要函数式显然效果更好,不需要借助redux,这就是我们为啥要在前面提到函数式编程涉及状态管理问题,就是要在这里跟react hook做个比较。

到这里我们知道了这个useState,多了一个useState让函数式创建类有了自己的持久状态。那么在函数式里面我们如何做到class组件中的setState呢?

function App(props) {
let [name, setName] = useState('dqhan');
let handleChangeName = useCallback(() => {
setName(preState => {
let updatedValues = {
newValue: '我变了'
}
return { ...{ preState }, ...updatedValues }
})
})
function click1(params) {
setName('我变了1')
}
return <div>
<p>{`hello~${name}`}</p>
<button onClick={handleChangeName}>Click1</button>
<button onClick={click1}>Click2</button>
</div>;
}

这中方式已经实现了状态整合,但是我们如果模拟一个state呢,来统一管理state呢,我们可以这么实现

function App(props) {
let [state, setState] = useState({
name: 'dqhan'
});
let handleChangeName = useCallback(() => {
setState(preState => {
let updatedValues = {
name: '我变了'
}
return { ...preState, ...updatedValues }
})
})
return <div>
<p>{`hello~${state.name}`}</p>
<button onClick={handleChangeName}>Click</button>
</div>;
}

到目前为止,已经知道了react hook中如何使用state,那么周期函数呢,那么就涉及另一个钩子useEffect

2.useEffect

作用:周期函数

useEffect涉及三个周期函数 componentDidMount 、componentDidUpdate、 compinentWillUmount 我们看一个简单的实例

function App() {
var [count, setCount] = useState(0);
useEffect(() => {
console.log(`update--${count}`);
}, [count]);
return (
<div>
<button onClick={() => setCount(count + 1)}>Click</button>
</div>
);
}
下面我们来了解一下react hooks的周期函数,他是如何工作的
function App() {
let [count, setCount] = useState(0);
useEffect(
() => {
//默认每一次渲染都需要执行的方法
console.log('didmount')
//如果需要实现componentWillComponent,则return 一个函数即可
return function unmount() {
console.log('unmount')
}
}
) let handleSetCount = useCallback(() => {
setCount((preCount) => {
let updatedCount = preCount + 1;
return updatedCount;
});
}) return <React.Fragment>
{console.log('render')}
<p>{`${count}`}</p>
<button onClick={handleSetCount}>Click</button>
</React.Fragment>
}

我们可以看一下执行周期

第一次渲染时候执行 render  didmount

点击事件执行顺序 render unmount didmount

不难看出,每一次渲染我们都会执行render进行渲染,然后清除掉上一次的useEffect,然后渲染完成之后重新执行useEffect

这样通过一个useEffec可以默认执行两个周期函数,也就是当我们需要对组件添加一些需要当组件卸载时候清除掉的功能时候,这个是很方便的,常见的就是setTimeout setIntrval等定时器

但是比如一个component渲染之后我们通常会发送一个请求来请求数据,然后重写渲染这个组件,这样会造成死循环怎么办,我们可以在useEffect后添加第二个参数

阻止useEffect每一次都要执行

function App() {
let [count, setCount] = useState(0);
useEffect(
() => {
//默认每一次渲染都需要执行的方法
console.log('didmount')
//如果需要实现componentWillComponent,则return 一个函数即可
return function unmount() {
console.log('unmount')
}
},
[setCount]
) let handleSetCount = useCallback(() => {
setCount((preCount) => {
let updatedCount = preCount + 1;
return updatedCount;
});
}) return <React.Fragment>
{console.log('render')}
<p>{`${count}`}</p>
<button onClick={handleSetCount}>Click</button>
</React.Fragment>
}

当传入第二个参数得值不变得时候就会跳过useEffect函数执行

如何模拟componentDidMount与componentWillUmount,第二个参数我们传一个空数组,这样就可以实现仅当组件渲染跟组件卸载得时候执行

function App() {
let [count, setCount] = useState(0);
useEffect(
() => {
//默认每一次渲染都需要执行的方法
console.log('didmount')
//如果需要实现componentWillComponent,则return 一个函数即可
return function unmount() {
console.log('unmount')
}
},
[]
) let handleSetCount = useCallback(() => {
setCount((preCount) => {
let updatedCount = preCount + 1;
return updatedCount;
});
}) return <React.Fragment>
{console.log('render')}
<p>{`${count}`}</p>
<button onClick={handleSetCount}>Click</button>
</React.Fragment>
}

不过,这隐藏了一个问题:传递空数组容易出现问题。如果咱们添加了依赖项,那么很容易忘记向其中添加项,如果错过了一个依赖项,那么该值将在下一次运行useEffect时失效,并且可能会导致一些奇怪的问题。

常见得就是当我们想用父组件调用子组件时候使用得ref,或者我们要获取dom焦点时

function App() {
let [count, setCount] = useState(0);
let [value, setValue] = useState('')
const inputRef = useRef(); useEffect(
() => {
//默认每一次渲染都需要执行的方法
console.log('didmount') //如果需要实现componentWillComponent,则return 一个函数即可
return function unmount() {
console.log('unmount')
}
},
[inputRef]
) let handleSetCount = useCallback(() => {
setCount((preCount) => {
let updatedCount = preCount + 1;
return updatedCount;
});
}) let handleSetValue = function (e) {
setValue(e.target.value);
} return <React.Fragment>
{console.log('render')}
<p>{`${count}`}</p>
<input
ref={inputRef}
value={value}
onChange={handleSetValue}
></input>
<button
ref={inputRef}
onClick={handleSetCount}
>Click</button> </React.Fragment>
}

下面我们实现一个请求实例

请求实例

function App() {
let [data, setData] = useState(null);
useEffect(() => {
const fetchData = async () => {
const result = await axios(config);
setData(result);
}; fetchData();
}, []); return <div></div>;
}

利用hook可以做到分离接口

function useFetchHook(config, watch) {
let [data, setData] = useState(null);
let [status, setStatus] = useState(0);
useEffect(
() => {
const fetchData = async () => {
try {
const result = await axios(config);
setData(result);
setStatus(0);
} catch (e) {
setStatus(1);
}
fetchData();
};
},
watch ? [...watch] : []
); return { data, status };
}

现在我们整体上知道了useState useEffect怎么使用了,我们来自己实现一个简易的

实现useState

var val;
function useState(initVal) {
let resultVal = val || initVal;
function setVal(newVal) {
resultVal = newVal;
render();
}
return [resultVal, setVal]
}

实现useEffect

var watchArr = [];
function useEffect(fn, watch) {
var hasWatchChange = true;
hasWatchChange = watchArr && watch.every((val, i) => val === watchArr[i])
if (hasWatchChange) {
fn();
watchArr = watch;
}
}

hooks里面最常用的两个API就是useState与useEffect,现在是不是已经了解了呢,下面我们介绍一些其他API

3.useContext

作用:越级别获取组件内容

类组件中我们也常用context,类组件实现方式


 const AppContext = React.createContext('target');
class App extends React.Component {
constructor(props) {
super(props)
}
render() {
return (
<AppContext.Provider value="dark">
<Target />
</AppContext.Provider>
);
}
} class Target extends React.Component {
//通过定义静态属性 contextType 来订阅
//没有定义是获取不到的
static contextType = AppContext;
render() {
console.log(this.context);
return <div></div>;
}
}

Hooks实现方式

const AppContext = React.createContext('target');

function App() {
useEffect(
() => { },
[]
);
return <AppContext.Provider value="dark">
<Target />
</AppContext.Provider>;
} function Target() {
const value = useContext(AppContext);
console.log(value);
return <div></div>;
}

在需要订阅多个 context 的时候,就更能体现出useContext的优势。

传统的实现方式

function App() {
return <CurrentUser.Consumer>
{
user => <Notifications.Consumer>
{notifications =>
<header>
Welcome back, {user.name}!
You have {notifications.length} notifications.
</header>
}
</Notifications.Consumer>
}
</CurrentUser.Consumer>
}

hooks实现

function App() {
const user = useContext(CurrentUser);
const notifications = useContext(Notifications); return (
<header>
Welcome back, {user.name}!
You have {notifications.length} notifications.
</header>
);
}

是不是比传统的要简单的多

4.useReducer

作用:复杂状态管理,跟redux本质上是一样的

函数式组件如果涉及到状态管理,我们需要借助redux,那么hooks需要吗,答案也是一样的,简单的状态管理我们可以通过useState来进行管理,如果比较复杂的状态管理呢,react hook给我们提供了方法 useReducer

function init(initialCount) {
return { count: initialCount };
} function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
case 'reset':
return init(action.payload);
default:
throw new Error();
}
} function Counter({ initialCount }) {
const [state, dispatch] = useReducer(reducer, initialCount, init);
return (
<>
Count: {state.count}
<button
onClick={() => dispatch({ type: 'reset', payload: initialCount })}>
Reset
</button>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
</>
);
}

5.useCallback

作用:提升性能,缓存事件,减少没必要的渲染

当我们使用类组件创建时,我们会怎么绑定事件呢

class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div>
<p>{`hello~${name}`}</p>
<button onClick={() => { console.log('click') }}>Click</button>
</div>
}
}

这样写会导致什么结果呢,就是当渲染的时候react会认为每一次绑定的事件都是新的,从而从新进行计算

改进如下

class App extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log('click')
}
render() {
return <div>
<p>{`hello~${name}`}</p>
<button onClick={this.handleClick}>Click</button>
</div>
}
}

我们讲触发函数绑定在this上,来缓存这个方法

hooks

function App() {
let [count, setCount] = useState(0);
return <div>
<button onClick={() => setCount(1)} ></button>
</div>
}

同样的问题这么写也是存在的,改进如下

function App() {
let [count, setCount] = useState(0);
let handleSetCount = useCallback(() => {
setCount(1);
})
return <div>
<button onClick={handleSetCount} ></button>
</div>
}

我们通过useCallback来缓存这个事件达到优化效果

6.useMemo

作用:提升性能,选择性的渲染变化组件

function App(target, target2) {
const target = useMemo(() => {
return <Target />
}, [target])
const target2 = useMemo(() => {
return <Target2 />
}, [target2])
return <div>
{target}
{target2}
</div>
}

当target变化仅渲染Target组件,同理也作用与Target2组件

React.memo

作用:提升性能

如果想实现class中的shouldComponentUpdate方法呢 ,区别是它只能比较 props,不会比较 state:

const App = React.mome((target, target2) => {
const target = useMemo(() => {
return <Target />
}, [target])
const target2 = useMemo(() => {
return <Target2 />
}, [target2])
return <div>
{target}
{target2}
</div>
})

7.useRef

作用:获取dom依赖关系

类组件实现方式

class App extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
} componentDidMount() {
this.myRef.current.focus();
} render() {
return <input ref={this.myRef} type="text" />;
}
}

hooks

function App() {
let [value, setValue] = useState('')
const inputRef = useRef(); useEffect(
() => {
},
[inputRef]
) let handleSetValue = function (e) {
setValue(e.target.value);
} return <React.Fragment>
<input
ref={inputRef}
value={value}
onChange={handleSetValue}
></input> </React.Fragment>
}

好了hooks的基本使用方式就介绍完了,现在你对hook多少能了解了一些吧~

代码地址:https://github.com/Dqhan/React

 
 
 

React hooks详解的更多相关文章

  1. React.Children详解

    React.Children提供了处理this.props.children的工具,this.props.children可以任何数据(组件.字符串.函数等等).React.children有5个方法 ...

  2. vue和react全面对比(详解)

    vue和react对比(详解) 放两张图镇压小妖怪 本文先讲共同之处, 再分析区别 大纲在此: 共同点: a.都使用虚拟dom b.提供了响应式和组件化的视图组件 c.注意力集中保持在核心库,而将其他 ...

  3. React Native组件、生命周期及属性传值props详解

    创建组件的三种方式 第一种:通过ES6的方式创建 /** * 方式一 :ES6 */ export default class HelloComponent extends Component { r ...

  4. 4-13 Webpacker-React.js; 用React做一个下拉表格的功能: <详解>

    Rails5.1增加了Webpacker: Webpacker essentially is the decisions made by the Rails team and bundled up i ...

  5. react基本demo详解

    一.react的优势 1.React速度很快:它并不直接对DOM进行操作,引入了一个叫做虚拟DOM的概念,安插在javascript逻辑和实际的DOM之间,性能好. 2.跨浏览器兼容:虚拟DOM帮助我 ...

  6. React 实践心得:react-redux 之 connect 方法详解

    Redux 是「React 全家桶」中极为重要的一员,它试图为 React 应用提供「可预测化的状态管理」机制. Redux 本身足够简单,除了 React,它还能够支持其他界面框架.所以如果要将 R ...

  7. React—组件生命周期详解

    React—组件生命周期详解 转自 明明的博客  http://blog.csdn.net/slandove/article/details/50748473 (非原创) 版权声明:转载请注明出处,欢 ...

  8. react目录结构、demo实例详解、属性数据绑定方式

    1.目录结构 2.demo实例详解 a)创建Home.js import React, { Component } from 'react'; //创建一个组件必须要集成Component组件,且组件 ...

  9. 8分钟为你详解React、Angular、Vue三大前端技术

    [引言] 当前世界中,技术发展非常迅速并且变化迅速,开发者需要更多的开发工具来解决不同的问题.本文就对于当下主流的前端开发技术React.Vue.Angular这三个框架做个相对详尽的探究,目的是为了 ...

随机推荐

  1. python自动化测试之函数(匿名函数lambda和三目运算等(高级用法))

    ''' 匿名函数: lambda ''' def Add(a,b): print(a+b) Add(2,3) per = lambda a,b:a+b print(per(2,3)) ''' 三目运算 ...

  2. 查看github仓库的地址

  3. Pytorch collate_fn用法

    By default, Dataloader use collate_fn method to pack a series of images and target as tensors (first ...

  4. Jquery和js实现cookie操作手机浮层广告;附加:js获取、添加、删除cookie

    1.jquery cookie包实现手机上的浮层广告 <span style="font-size:18px;">$(document).ready(function( ...

  5. MyBatisUtil

    package com.it.util; import java.io.IOException; import java.io.Reader; import org.apache.ibatis.io. ...

  6. function_exists (),method_exists()与is_callable()的区别

    is_callable()函数要高级一些,它接受字符串变量形式的方法名作为 第一个参数,如果类方法存在并且可以调用,则返回true.如果要检测类中的方法是否能被调用,可以给函数传递一个数组而不是类的方 ...

  7. Java Random函数

    Java中存在着两种Random函数: 1.java.lang.Math.Random: 调用这个Math.Random()函数能够返回带正号的double值,该值大于等于0.0且小于1.0,即取值范 ...

  8. 云服务器——之Linux下安装tomcat

    在上一篇文章中已经准备好了tomcat安装的基本环境jdk的安装,那么我们现在来记录tomcat的安装. 第一步:下载tomcat安装包 http://tomcat.apache.org/ 第二步:通 ...

  9. CentOS7使用firewalld管理防火墙与端口

    firewalld的基本使用 启动: systemctl start firewalld 关闭: systemctl stop firewalld 查看状态: systemctl status fir ...

  10. 推拿O2O 想说爱你还不容易

    想说爱你还不容易" title="推拿O2O 想说爱你还不容易"> <屌丝男士>第四季最后一集里,乔杉终于圆了"大保健"的梦想,可惜 ...