总结:
-
函数式编程其实是一种编程思想,它追求更细的粒度,将应用拆分成一组组极小的单元函数,组合调用操作数据流;
-
它提倡着 纯函数 / 函数复合 / 数据不可变, 谨慎对待函数内的 状态共享 / 依赖外部 / 副作用;
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
Tips:
其实我们很难也不需要在面试过程中去完美地阐述出整套思想,这里也只是浅尝辄止,一些个人理解而已。博主也是初级小菜鸟,停留在表面而已,只求对大家能有所帮助,轻喷🤣;
我个人觉得: 这些编程范式之间,其实并不矛盾,各有各的 优劣势。
理解和学习它们的理念与优势,合理地 设计融合,将优秀的软件编程思想用于提升我们应用;
所有设计思想,最终的目标一定是使我们的应用更加 解耦颗粒化、易拓展、易测试、高复用,开发更为高效和安全;
);
}
export default ContextPage;
(3)useContext()钩子函数用来引入 Context 对象,并且获取到它的值 // 子组件,在子组件中使用孙组件
import React from ‘react’;
import ContextComponent2 from ‘./ContextComponent2’;
function ContextComponent () {
return (
);
}
export default ContextComponent;
// 孙组件,在孙组件中使用 Context 对象值
import React, { useContext } from ‘react’;
import ThemeContext from ‘./ThemeContext’;
function ContextComponent () {
const value = useContext(ThemeContext);
return (
);
}
export default ContextComponent;
四、useReducer
1、基础用法
比 useState 更适用的场景:例如 state 逻辑处理较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等;例子如下所示
import React, { useReducer } from ‘react’;
interface stateType {
count: number
}
interface actionType {
type: string
}
const initialState = { count: 0 };
const reducer = (state:stateType, action:actionType) => {
switch (action.type) {
case ‘increment’:
return { count: state.count + 1 };
case ‘decrement’:
return { count: state.count - 1 };
default:
throw new Error();
}
};
const UseReducer = () => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<button onClick={() => { dispatch({ type: ‘decrement’ }); }}>useReducer 减少
<button onClick={() => { dispatch({ type: ‘increment’ }); }}>useReducer 增加
);
};
export default UseReducer;
2、惰性初始化 state
interface stateType {
count: number
}
interface actionType {
type: string,
paylod?: number
}
const initCount =0
const init = (initCount:number)=>{
return {count:initCount}
}
const reducer = (state:stateType, action:actionType)=>{
switch(action.type){
case ‘increment’:
return {count: state.count + 1}
case ‘decrement’:
return {count: state.count - 1}
case ‘reset’:
return init(action.paylod || 0)
default:
throw new Error();
}
}
const UseReducer = () => {
const [state, dispatch] = useReducer(reducer,initCount,init)
return (
<button onClick={()=>{dispatch({type:‘decrement’})}}>useReducer 减少
<button onClick={()=>{dispatch({type:‘increment’})}}>useReducer 增加
<button onClick={()=>{dispatch({type:‘reset’,paylod:10 })}}>useReducer 增加
);
}
export default UseReducer;
五、Memo
如下所示,当父组件重新渲染时,子组件也会重新渲染,即使子组件的 props 和 state 都没有改变
import React, { memo, useState } from ‘react’;
// 子组件
const ChildComp = () => {
console.log(‘ChildComp…’);
return (
};
// 父组件
const Parent = () => {
const [count, setCount] = useState(0);
return (
);
};
export default Parent;
改进:我们可以使用 memo 包一层,就能解决上面的问题;但是仅仅解决父组件没有传参给子组件的情况以及父组件传简单类型的参数给子组件的情况(例如 string、number、boolean等);如果有传复杂属性应该使用 useCallback(回调事件)或者 useMemo(复杂属性)
// 子组件
const ChildComp = () => {
console.log(‘ChildComp…’);
return (
};
const MemoChildComp = memo(ChildComp);
六、useMemo
假设以下场景,父组件在调用子组件时传递 info 对象属性,点击父组件按钮时,发现控制台会打印出子组件被渲染的信息。
import React, { memo, useState } from ‘react’;
// 子组件
const ChildComp = (info:{info:{name: string, age: number}}) => {
console.log(‘ChildComp…’);
return (
};
const MemoChildComp = memo(ChildComp);
// 父组件
const Parent = () => {
const [count, setCount] = useState(0);
const [name] = useState(‘jack’);
const [age] = useState(11);
const info = { name, age };
return (
);
};
export default Parent;
分析原因:
点击父组件按钮,触发父组件重新渲染;父组件渲染,const info = { name, age } 一行会重新生成一个新对象,导致传递给子组件的 info 属性值变化,进而导致子组件重新渲染。
解决:
使用 useMemo 将对象属性包一层,useMemo 有两个参数:
-
第一个参数是个函数,返回的对象指向同一个引用,不会创建新对象;
-
第二个参数是个数组,只有数组中的变量改变时,第一个参数的函数才会返回一个新的对象。
import React, { memo, useMemo, useState } from ‘react’;
// 子组件
const ChildComp = (info:{info:{name: string, age: number}}) => {
console.log(‘ChildComp…’);
return (
};
const MemoChildComp = memo(ChildComp);
// 父组件
const Parent = () => {
const [count, setCount] = useState(0);
const [name] = useState(‘jack’);
const [age] = useState(11);
// 使用 useMemo 将对象属性包一层
const info = useMemo(() => ({ name, age }), [name, age]);
return (
);
};
export default Parent;
七 、useCallback
接着第六章节的例子,假设需要将事件传给子组件,如下所示,当点击父组件按钮时,发现控制台会打印出子组件被渲染的信息,说明子组件又被重新渲染了。
import React, { memo, useMemo, useState } from ‘react’;
// 子组件
const ChildComp = (props:any) => {
console.log(‘ChildComp…’);
return (
};
const MemoChildComp = memo(ChildComp);
// 父组件
const Parent = () => {
const [count, setCount] = useState(0);
const [name] = useState(‘jack’);
const [age] = useState(11);
const info = useMemo(() => ({ name, age }), [name, age]);
const changeName = () => {
console.log(‘输出名称…’);
};
return (
);
};
export default Parent;
分析下原因:
点击父组件按钮,改变了父组件中 count 变量值(父组件的 state 值),进而导致父组件重新渲染;父组件重新渲染时,会重新创建 changeName 函数,即传给子组件的 changeName 属性发生了变化,导致子组件渲染;
解决:
修改父组件的 changeName 方法,用 useCallback 钩子函数包裹一层, useCallback 参数与 useMemo 类似
import React, { memo, useCallback, useMemo, useState } from ‘react’;
// 子组件
const ChildComp = (props:any) => {
console.log(‘ChildComp…’);
return (
};
const MemoChildComp = memo(ChildComp);
// 父组件
const Parent = () => {
const [count, setCount] = useState(0);
const [name] = useState(‘jack’);
const [age] = useState(11);
const info = useMemo(() => ({ name, age }), [name, age]);
const changeName = useCallback(() => {
console.log(‘输出名称…’);
}, []);
return (
);
};
export default Parent;
八、useRef
以下分别介绍 useRef 的两个使用场景:
1、指向 dom 元素
如下所示,使用 useRef 创建的变量指向一个 input 元素,并在页面渲染后使 input 聚焦
import React, { useRef, useEffect } from ‘react’;
const Page1 = () => {
const myRef = useRef(null);
useEffect(() => {
myRef?.current?.focus();
});
return (
UseRef:
);
};
export default Page1;
2、存放变量
useRef 在 react hook 中的作用, 正如官网说的, 它像一个变量, 类似于 this , 它就像一个盒子, 你可以存放任何东西. createRef 每次渲染都会返回一个新的引用,而 useRef 每次都会返回相同的引用,如下例子所示:
import React, { useRef, useEffect, useState } from ‘react’;
const Page1 = () => {
const myRef2 = useRef(0);
const [count, setCount] = useState(0)
useEffect(()=>{
myRef2.current = count;
});
function handleClick(){
setTimeout(()=>{
console.log(count); // 3
console.log(myRef2.current); // 6
},3000)
}
return (
);
}
export default Page1;
九、useImperativeHandle
使用场景:通过 ref 获取到的是整个 dom 节点,通过 useImperativeHandle 可以控制只暴露一部分方法和属性,而不是整个 dom 节点。
十、useLayoutEffect
总结一下
面试前要精心做好准备,简历上写的知识点和原理都需要准备好,项目上多想想难点和亮点,这是面试时能和别人不一样的地方。
还有就是表现出自己的谦虚好学,以及对于未来持续进阶的规划,企业招人更偏爱稳定的人。
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
万事开头难,但是程序员这一条路坚持几年后发展空间还是非常大的,一切重在坚持。
为了帮助大家更好更高效的准备面试,特别整理了《前端工程师面试手册》电子稿文件。
前端面试题汇总
JavaScript
性能
linux
前端资料汇总
前端工程师岗位缺口一直很大,符合岗位要求的人越来越少,所以学习前端的小伙伴要注意了,一定要把技能学到扎实,做有含金量的项目,这样在找工作的时候无论遇到什么情况,问题都不会大。