换了工做的新环境后,感触仍是蛮深的,能感觉到的是不少人对待工做的极致,相比以前而言,会更加的适合对工做充满激情的人 🤔;一个最大的挑战就是技术栈了,从本身熟练的Vue转换到React,快速的学习和上手,坑天然也是踩了很多;html
在实际项目中,咱们会将多一个完整的功能拆分红多个模块,用来处理逻辑,中台开发中,最多见的莫过于“搜索列表”+“添加数据”等操做;react
简单描述下交互git
需求、交互都很普通,可是正是如此,我使用useCallback
掉入了大坑github
const OperationList: React.FC = () => {
const [showCreateGood, setShowCreateGood] = useState(false);
const [searchQuery, setSearchQuery] = useState<ListQueryParams>(initOpQuery);
const initFetchData = async (query?: ListQueryParams) => {
const searchParams = query ?? searchQuery;
// 执行请求操做 省略
};
useEffect(() => {
initFetchData(searchQuery);
}, []);
// 搜索内容
const onSearch = (value?: ListQueryParams) =>
new Promise((resolve, reject) => {
setSearchQuery(value);
initFetchData(value)
.then(res => resolve(res))
.catch(error => reject(new Error(error)));
});
return (
<Card> //...... <Button onClick={onSearch}></Button> {<CreateGoodsSold visible={showCreateGood} setVisible={setShowCreateGood} createSuccess={initFetchData} />} </Card>
);
};
复制代码
// eslint-disable-next-line max-lines-per-function
const CreateGoodsSoldOut: React.FC<CreateGoodsSoldOutProps> = ({ visible, setVisible, createSuccess }) => {
const [form] = Form.useForm();
// 提交站内信内容
const onSubmit = async (extraParams = { flag: 0 }) => {
let postParams: postCreateNotice = form.getFieldsValue();
try {
await createSoldOut({ ...postParams, ...extraParams }); ;
//添加成功 调用父组件的方法
createSuccess();
} catch (error) {
}
};
// 表单校验完成 + 弹框提示
const onfinish =useCallback( () => {
Modal.confirm({
title: '',
content: 'ccc?',
icon: null,
okText: '肯定提交',
cancelText: '取消',
centered: true,
onOk: () => {
onSubmit();
},
},[])
return (
<Drawer destroyOnClose forceRender width={700} visible={visible} onCancel={clearForm} onOk={() => { form.submit(); }} okButtonProps={{ htmlType: 'submit' }} > // ......表单收集项目 </Drawer>
);
};
复制代码
内容嵌套有点乱?? 来张图数组
这看似普通的代码 在一次次的进行校验后居然出现了问题缓存
searchQuery
是保存当前搜索数据的searchQuery
在请求函数中始终不是当前最新的数据内部的函数的searchQuery
和外部的searchQuery
是不一样步的,也就是函数内部没有取到最新的值性能优化
咱们梳理逻辑,父组件定义了一个函数initFetchData
传给子组件,子组件在onSubmit 中调用了这个函数,可是 在onfinish
中咱们使用了useCallback
,此时useCallback
传递的第二个数组是空,也就是不依赖的,只在初始渲染时候进行定义,后面任何值变化时候都不会引发这个函数变化,为此产生了疑问? useCallback
定义的无依赖的函数,对于内部所调用的函数的值是否有所影响,也就是initFetchData
中调用的是初始保留的值? 为此 进行了一番探究markdown
根据上述问题的疑问,进行demo的测试,咱们疑问点在于:闭包
useCallback
包装的函数调用父组件函数时候,父组件函数内部的数值获取,即被useCallback
包裹的函数,内部函数调用的做用域;// 父组件的定义
const UseCallBackDemo = () =>{
const [query,setQuery] = useState(null)
const parentFun = (value)=>{
console.log(value,query)
}
const changeQuery = () =>{
setQuery(222)
}
return(<> 测试useCallBack <button onClick={changeQuery}>更改query的值{query}</button> <Children1 parentsMethod={parentFun}></Children1> <Children2 parentsMethod={parentFun}></Children2> </>)
}
复制代码
clickParent
未被useCallback
包裹clickParent
被useCallback
包裹// 两个子组件
const Children1 =memo( ({ parentsMethod })=>{
const clickParent = ()=>{
parentsMethod("子组件1调用了");
}
return(<div> 我是子组件1 <button onClick={clickParent}>我是子组件1 调用parentsMethod</button> </div>)
})
const Children2 =memo( ({ parentsMethod })=>{
//
const clickParent = useCallback(()=>{
parentsMethod("子组件2调用了 useCallback");
},[])
return(<div> 我是子组件2 <button onClick={clickParent}>我是子组件2 调用parentsMethod</button> </div>)
})
复制代码
点击按钮后async
子组件2中使用了useCallback的无依赖函数,调用父组件时候,query仍是初始的值,并未获得更新;
<Children2 parentsMethod={parentFun} query={queru}></Children2>
const Children2 =memo( ({ parentsMethod,query })=>{
const clickParent = useCallback(()=>{
parentsMethod("子组件2调用了 useCallback");
},[query])
return(<div> 我是子组件2 <button onClick={clickParent}>我是子组件2 调用parentsMethod</button> </div>)
})
复制代码
使用了useCallback
进行包装的函数,会影响到其内部的全部调用函数 带着这个猜测进行debugger
查看函数上下文和执行栈
当执行到Children2
的函数时候,此时parentsMethod
的上下文和做用域以下;此时parentsMethods
的做用域上是产生了一个闭包,也就是定义的query
的初始值;
执行到父组件函数内部
也会存在闭包,做用域使用的是范围是最新的
执行到parents的时候
````useCallback```优化性能,可是使用可能致使出现错误,影响内部调用的函数做用域,所以谨慎使用,若是存在依赖函数,必定要进行相关依赖函数的监听;
useCallback 的做用在于利用 memoize 减小无效的 re-render,来达到性能优化的做用,callback 内部对 state 的访问依赖于 JavaScript 函数的闭包。若是但愿 callback 不变,那么访问的以前那个 callback 函数闭包中的 state 会永远是当时的值。
useCallback
的实现有中,分为mountHook
和updateHook
;
function mountCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
const hook = mountWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
// 利用memoizedState 缓存mount阶段时候的变量
hook.memoizedState = [callback, nextDeps];
return callback;
}
复制代码
function updateCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
const hook = updateWorkInProgressHook();
// 获取下一个nextDeps
const nextDeps = deps === undefined ? null : deps;
// 获取前一个存储的内部数值
const prevState = hook.memoizedState;
// 若是前一个不是空的 则进行浅比较
if (prevState !== null) {
if (nextDeps !== null) {
const prevDeps: Array<mixed> | null = prevState[1];
if (areHookInputsEqual(nextDeps, prevDeps)) {
return prevState[0];
}
}
}
// 存储当前的这个内容
hook.memoizedState = [callback, nextDeps];
return callback;
}
复制代码