我使用 react 开发已经 1 年的时间了,不能说很精通,不过在使用的过程中,确实领悟和总结了一些小技巧,可以加快我们后续的开发
我们在使用 react 开发 function comppoent 时,必然绕不开 useState ,可谓是随处可见,那么 useState 中有哪些坑和需要注意的地方呢?
有些使用 react 时间较短的同学,经常会这样写,在调用 set 方法设置 state 的值后,立刻去获取这个值:
const [data, setData] = useState();
const func = () => {
console.log(data);
};
useEffect(() => {
setData({ name: '蚊子' });
func();
}, []);
然而在使用 log 输出时,却发现没有变化。这是因为 useState 中的 set 是异步操作,而函数 func 的调用是同步的,这就导致 func()要比 set 先执行。
那么如何解决这个异步的问题呢?这里有几种方式可以参考:
useEffect(() => {
if (data) {
// 当满足条件时,执行func()
func();
}
}, [data]);
useEffect(() => {
const data = { name: '蚊子' };
setData(data);
func(data);
}, []);
延时触发也可以解决这个问题,但是最不建议这种方式,一个异步问题,如果再用一个延时更大的异步操作来兜底,可能还会产生其他一些未知的问题。
useEffect(() => {
setData({ name: '蚊子' });
setTimeout(func, 10);
}, []);
我们想要延迟设置一些数据,例如让一个计数器固定简单的增加+1,我们可能会这样写:
const [count, setCount] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setCount(count + 1);
}, 1000);
return () => clearInterval(timer); // 注意清除定时器
}, []);
可是运行时我们发现,计数器只是从 0 涨到了 1,然后就再也不增加了,在 setInterval 中的回调函数里打 log,发现回调函数还在执行,可是就是 count 没有发生变化。
这是因为 useEffect 的第二个参数 依赖项 导致的,在我们上面的示例中,依赖项为空数组,这表示在第一次初始化后,内部就再也不发生变化,那么 count 依赖的就永远是第最开始的初始值。
可是如何实现在定时器中持续操作 state 呢?这里有几种解决方式可供参考:
刚才我们也说是因为依赖项,导致每次读取都是最开始初始化时的值,那么我们就可以把 count 添加到依赖项中:
const [count, setCount] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setCount(count + 1);
}, 1000);
return () => clearInterval(timer);
}, [count]);
这样就能实现计数器一直增加了。
但这样会导致另一个问题:每次 count 发生变化时,都会先执行 clearInterval ,然后再重启一个新的定时器 setInterval ,setInterval 就蜕变成了 setTimeout 了,只执行一次 setInterval 就会被清理掉。因此这里其实换成 setTimeout 即可:
useEffect(() => {
const timer = setTimeout(() => {
setCount(count + 1);
}, 1000);
return () => clearTimeout(timer);
}, [count]);
还有一种更简单粗暴的方式,就是不添加任何依赖项,但这种方式,会导致所有的 state 变化,都会触发 useEffect 的重新执行:
useEffect(() => {
const timer = setTimeout(() => {
setCount(count + 1);
}, 1000);
return () => clearTimeout(timer);
});
这种没有任何依赖项的方式,建议在组件中只有一个 count 的 state 中使用!
我们可以利用 useRef 的特性,来保存刚才发生变化的 state 值,下次使用时,可以直接从 useRef 中获取。useRef 中的值并不受依赖项的限制:
const [count, setCount] = useState(0);
const countRef = useRef(0); // 创建一个ref来保存count的值
useEffect(() => {
countRef.current = count; // count发生变化时,将其存储起来
}, [count]);
useEffect(() => {
const timer = setInterval(() => {
setCount(countRef.current + 1); // 每次从countRef.current中获取到最新的值
}, 1000);
return () => clearInterval(timer);
}, []);
刚才 useRef 已经小试牛刀,试用了一下。现在我们再来看看它具体的用法。
useRef 可以保存一切可变的值,例如上面 state 中的值,同时还有定时器的 timer,requestAnimationFrame 的 id 等。例如在递归的操作中,timer 和 id 每次都是不一样的,当取消的时候,应当取消哪个就很头疼,这里我们就可以使用 useRef 来进行存储:
const requestRef = useRef(0);
// 开始渲染
const render = () => {
console.log('render', Date.now());
requestRef.current = requestAnimationFrame(() => {
render();
});
};
// 取消渲染
const cancelRender = () => {
cancelAnimationFrame(requestRef.current);
};
我们在上面的例子中,都是只保存一个值,然后再被另一个新值给覆盖掉。可是如果我们想同时保存多个值时,怎么办呢?
如果是单纯的数据的话,那把 ref 初始化成数组,然后按照数组的方式操作即可。
const listRef = useRef([]);
useEffect(() => {
const timer = setInterval(() => {
listRef.current.push(Date.now());
}, 1000);
return () => clearInterval(timer);
}, []);
可是我们有一个 dom 元素列表,想把所有的 dom 元素都存储起来?
const domListRef = useRef([]);
const getDomList = useCallback((dom: htmlDivElement | null) => {
if (dom) {
domListRef.push(dom);
}
}, []);
<>
{
list.map((item) => <div key={item.ename} ref={getDomList}></div>);
}
</>
这里要特别注意,getDomList 方法要用 useCallback 包裹起来,否则每次 state 变化时,都会导致 domListRef 数据的增加!
我们通过第 1 节和第 2 节了解了 useEffect 中的 setTimeout(setInterval)和 useRef 的表现,基于这两者,我们可以自己实现一个 useTimeout 或者 useInterval。
useInterval 的实现:
const useInterval = (callback, delay) => {
const saveCallback = useRef();
useEffect(() => {
// 每次callback发生变化时,都将最新的callback保存起来
saveCallback.current = callback;
});
useEffect(() => {
function tick() {
saveCallback.current();
}
if (delay !== null) {
let id = setInterval(tick, delay); // setInterval只运行ref.current里的方法
return () => clearInterval(id);
}
}, [delay]);
};
在 useInterval 中执行 state 的 set 操作时,就不用再关心 state 是否会更新的问题了:
const [count, setCount] = useState(0);
useInterval(() => {
setCount(count + 1);
}, 1000);
我们在引入组件并渲染时,会按照引入的位置进行渲染。不过对于一些公共组件,例如弹窗、toast、悬浮挂件角标等,不希望这些组件的样式收到父级元素的样式影响(如 overflow: hidden , transform (父级元素有 transform 样式时,"position: fixed"会降级为"position: absolute"), z-index 等),最好她可以渲染到根目录或者其他指定的目录中。这时应该怎么办呢?
我们就用到 createPortal 了,该方法的使用规则非常简单,就 2 个参数:
要渲染的组件;
要挂载的 DOM 节点;
代码说明下:
import { createPortal } from 'react';
const Modal = () => {
return createPortal(
<div className="i-modal">
<div className="i-modal-content"></div>
</div>,
document.body,
);
};
export default Modal;
这个 <Modal /> 组件可以在任何地方引用,然后渲染时,会被渲染到 document.body 的最后(类似于 appendChild 操作):
import Modal from './modal';
const User = () => {
return (
<div className="user">
<Modal />
</div>
);
};
其实在 react 中还有很多的小技巧操作,如果我们熟悉这些小技巧后,就能快速地用 react 搭建我们的应用。
原文 https://www.xiabingbao.com/post/react/react-use-tips.html
一直以来进行了比较多的微信小程序开发... 总会接触到一些和官方组件或 api 相关或其无法解决的需求,于是决定在这里小小的整理一下微信小程序开发的一些技巧
小程序提供onShareAppMessage 函数,此函数只支持分享给我微信朋友,小程序如何分享到朋友圈呢?使用canvas绘制一张图片,并用wx.previewImage预览图片,然后长按图片保存图片到手机。
前端新手程序员不知道的 20个小技巧:作为前端开发者,使用双显示器能大幅提高开发效率、学编程最好的语言不是PHP,是English、东西交付之前偷偷测试一遍、问别人之前最好先自己百度,google一下、把觉得不靠谱的需求放到最后做,很可能到时候需求就变了...
本地的 IP 地址是分配给你计算机上的内部硬件或虚拟网卡的本地/私有 IP 地址。根据你的 LAN 配置,上述 IP 地址可能是静态或动态的。公共的 IP 地址是你的 Internet 服务提供商(ISP)为你分配的公共/外部 IP 地址。
使用 :not() 在菜单上应用/取消应用边框;给body添加行高;所有一切都垂直居中;逗号分隔的列表;使用负的 nth-child 选择项目;对图标使用SVG;优化显示文本;对纯CSS滑块使用 max-height;继承 box-sizing
禁用右键点击;禁用搜索文本框;新窗口打开链接;检测浏览器;预加载图片;样式筛选;列高度相同;字体大小调整;返回页面顶部;获取鼠标的xy坐标;验证元素是否为空;替换元素
为你网站的用户留下良好的第一印象是非常必要的。随着商业领域的竞争,拥有一个吸引人的网站可以帮助你脱颖而出。研究表明,如果加载时间超过3秒,会有 40% 的用户放弃访问你的网站
清除浮动主要用于子元素浮动(float)之后,父元素无法撑起高度和宽度。文字少时居中,多时靠左因为div嵌套着p,所以p的尺寸并不会超过div。但是要注意,当p的内容为英文单词组成的时候
这次我们主要来分享11个在日常教程中不常被提及的JavaScript小技巧,他们往往在我们的日常工作中经常出现,但是我们又很容易忽略。Set类型是在ES6中新增的,它类似于数组,但是成员的值都是唯一的
为什么要在JavaScript里写CSS?避免命名全局污染,条件和动态样式(比如选择主题色之类的),在框架层面进行限制或补充(比如补全供应商前缀),避免业务人员使用奇技淫巧
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!