首页
工具
心境语句
相册
建站轨迹
关于
Search
1
微信小程序:计算属性的两种体现方式及应用场景
1,620 阅读
2
Antd Upload 组件上传文件接收数据流并下载
1,112 阅读
3
[C#]使用dnSpy对目标程序(EXE或DLL)进行反编译修改并编译运行
590 阅读
4
C#插件火车头采集器动态切换代理IP,及自动切换UserAgent
583 阅读
5
ADODB.Connection 错误 800a0e7a 未找到提供程序。该程序可能未正确安装解决方法
529 阅读
react
typecho
ASP
Centos
MYSQL
PHP
Sql server
Javascript
nodejs
数据采集
.NET
git
编程算法
管理及流程
Vue
微信小程序
android
python
mongodb
登录
Search
标签搜索
kotlin
node-sass
nuxtjs
C#火车头插件
火车头采集器
火车头代理
C#反编译
程序逆向
dnSpy教程
Antd
InputNumber
NPM教程
NPM命令
rrweb教程
git慢
git镜像
vim命令
git命令
网页音乐插件
网页播放器
Elysian
累计撰写
74
篇文章
累计收到
0
条评论
首页
栏目
react
typecho
ASP
Centos
MYSQL
PHP
Sql server
Javascript
nodejs
数据采集
.NET
git
编程算法
管理及流程
Vue
微信小程序
android
python
mongodb
页面
工具
心境语句
相册
建站轨迹
关于
搜索到
8
篇与
react
的结果
2021-09-15
Antd中InputNumber组件数字限制小数位数
InputNumber组件强制限制小数,可以使用 formatter 属性配合正则来实现,但在实践中发现一个问题,比如限制的两位小数,当我输第三位小数的时候,组件会保存这个值,导致传给后台的值多出一位小数。在2.9.0之后,InputNumber 新增 parser 属性, 搭配 formatter 一起使用,可以较好的限制小数的位数。将formatter和parser属性都绑定同一个方法,在这个方法里实现:...... <InputNumber min={0} max={100} step={0.01} formatter={limitDecimals} parser={limitDecimals} />绑定方法:const limitDecimals = (value: string | number): string => { const reg = /^(\-)*(\d+)\.(\d\d).*$/; console.log(value); if(typeof value === 'string') { return !isNaN(Number(value)) ? value.replace(reg, '$1$2.$3') : '' } else if (typeof value === 'number') { return !isNaN(value) ? String(value).replace(reg, '$1$2.$3') : '' } else { return '' } };
2021年09月15日
419 阅读
0 评论
0 点赞
2021-09-11
如何把html字符串转成一个React组件?简单而轻量级HTML字符串到React元素转换库htmr
React对html支持前景dangerouslySetInnerHTMLreact提供了dangerouslySetInnerHTML属性,把html字符串转成react元素:安全性通常来讲,直接设置dangerouslySetInnerHTML存在风险,因为很容易无意中使用户暴露于跨站脚本(XSS) 的攻击。因此,你可以直接在 React 中设置 HTML,但当你想设置 dangerouslySetInnerHTML 时,需要向其传递包含 key 为 __html 的对象,以此来警示你。正如上面所说,直接使用dangerouslySetInnerHTML存在xss的风险。所以我们需要先对html字符串进行过滤、转换,再通过 React.createElement() 把字符串转成React组件。如果需要自己去实现这一步骤的话,可能会比较麻烦(因为还涉及字符串转dom、属性转React属性等操作)。下面会介绍一个实现这个功能的库htmr和内部原理。htmr安装方法$ yarn add htmr # or $ npm install htmr --save使用htmr接收两个参数,html字符串和一个配置对象options。html:stringoptions:Partial<HtmrOptions>={}下面着重介绍下HtmrOptions里面各个属性:preserveAttributes:Array<String | RegExp> - 默认情况下,htmr会将符合要求的html属性转换为React要求的驼峰式属性,如果某些属性不想转换,可以通过该属性来阻止React这个行为。transform - 接受键值对,这些键值对将用于 将节点(键)转换为自定义组件(值),可以使用它来通过自定义组件呈现特定的标签名称。例如,下面这个例子。定义了transform对象,目的是把p标签转成Paragraph组件,把a标签转成span标签:const transform = { p: Paragraph, a: 'span' } htmr('<p><a>Hello, world!</a></p>', {transform}) // 结果 => <Paragraph><span>Custom component</span></Paragraph>在 transform 里面有一个参数叫做defaultTransform, 以符号 _表示,它接受的参数跟React.createElement一致。这个参数非常有用,例如可以在富文本里面处理图片,把图片转成我们自定义的图片组件const transform = { // 参数跟React.createElement一致 _: (nodeName, props, children) => { if(nodeName === 'img) { let src = props.src; return <Image src={src}> } return React.createElement(nodeName, props, children); } }在 transform 里面还有一个参数叫 dangerouslySetChildren ,出于安全原因,默认情况下,htmr仅将危险标签内的样式标记的子项呈现在危险地设置为InnerHTML中。例如,下面例子设置dangerouslySetChildren:['code']:const html = '<div><code><span>xxx</span></code></div>' htmr(html, { dangerouslySetChildren: ['code'] }); // <div><code dangerouslySetInnerHTML={{__html: encode('<span>xxx</span>')}}>工具函数hypenColonToCamelCase把带中划线或者冒号的字符串转成驼峰式,如 color-profile => colorProfile,xlink:role => xlinkRole 。function hypenColonToCamelCase(str: string): string { return str.replace(/(-|:)(.)/g, (match, symbol, char) => { return char.toUpperCase(); }); }convertValuefunction convertValue(value: string): number | string { if (/^\d+$/.test(value)) { return Number(value); } return value.replace(/'/g, '"'); }convertStyle把行内样式字符串转成StyleObject类型:function convertStyle(styleStr: string): StyleObject { const style = {} as StyleObject; styleStr .split(';') .filter(style => style.trim() !== '') .forEach(declaration => { const rules = declaration.split(':'); if (rules.length > 1) { // 属性名 const prop = hypenColonToCamelCase(rules[0].trim()); const val = convertValue( rules .slice(1) .join(':') .trim() ); style[prop] = val; } }); return style; }内部原理htmlServer我们在上面例子用到的htmr函数其实就是htmlServer,它主要做了两件事情:html字符串转成dom;对dom所有节点做转换成符合要求的ReactElement;export default function htmrServer( html: string, options: Partial<HtmrOptions> = {} ) { if (typeof html !== 'string') { throw new TypeError('Expected HTML string'); } const doc = parseDocument(html.trim(), {}); // 1. const nodes = doc.childNodes.map((node, index) => // 2. toReactNode(node, index.toString(), options) ); return nodes.length === 1 ? nodes[0] : nodes; }htmlServer用到一个parseDocument方法,它是 htmlparser2导出的一个函数,能把html字符串转化成dom:import { parseDocument } from 'htmlparser2';toReactNode顾名思义,toReactNode是把dom转成ReactNode,也是这个库的核心。根据dom节点的type属性,做了分类处理:如果type 的值是 'script' 、'style' 和 'tag' 其中之一,执行如下操作:解码所有属性值;执行mapAttribute(把属性转成React属性);根据transform转化标签;const node: HTMLNode = childNode as any; const { name, attribs } = node; // decode all attribute value Object.keys(attribs).forEach((key) => { attribs[key] = decode(attribs[key]); }); const props = Object.assign( {}, mapAttribute(name, attribs, preserveAttributes, getPropName), { key } ); /** * const transform = { * p: Paragraph, * a: 'span', * }; * 例如把 p标签转成 Paragraph标签,a转成span */ const customElement = transform[name];判断当前标签是否在dangerouslySetChildren列表,是的话塞到dangerouslySetInnerHTMLif (dangerouslySetChildren.indexOf(name) > -1) { // Tag can have empty children if (node.children.length > 0) { const childNode: TextNode = node.children[0] as any; const html = name === 'style' || name === 'script' ? // preserve encoding on style & script tag childNode.data.trim() : encode(childNode.data.trim()); props.dangerouslySetInnerHTML = { __html: html }; } return customElement ? React.createElement(customElement as any, props, null) : defaultTransform ? defaultTransform(name, props, null) : React.createElement(name, props, null); }对children节点执行toReactNode;如果存在transform,转化成对应ReactElement并返回;如果存在defaultTransform ,调用defaultTransform 并返回;如果不存在transform和defaultTransform,执行React.createElement;// 5. const childNodes = node.children .map((node, index) => toReactNode(node, index.toString(), options)) .filter(Boolean); // self closing component doesn't have children const children = childNodes.length === 0 ? null : childNodes; // 6. if (customElement) { return React.createElement(customElement as any, props, children); } // 7. if (defaultTransform) { return defaultTransform(name, props, children); } // 8. return React.createElement(name, props, children);如果type是'text',则处理很简单:const node: TextNode = childNode as any; let str = node.data; if (node.parent && TABLE_ELEMENTS.indexOf(node.parent.name) > -1) { str = str.trim(); if (str === '') { return null; } } str = decode(str); return defaultTransform ? defaultTransform(str) : str;接下来,了解一下第2步提到的mapAttribute是如何把html属性转成React属性的。mapAttribute首先,先贴上代码:Object.keys(attrs).reduce((result, attr) => { // 1 if (/^on.*/.test(attr)) { return result; } // 2 let attributeName = attr; if (!/^(data|aria)-/.test(attr)) { // Allow preserving non-standard attribute, e.g: `ng-if` const preserved = preserveAttributes.filter(at => { if (at instanceof RegExp) { return at.test(attr); } return at === attr; }); if (preserved.length === 0) { attributeName = hypenColonToCamelCase(attr); } } // 3 const name = getPropName(originalTag, attributeName); // 4 if (name === 'style') { result[name] = convertStyle(attrs.style!); } // 5 else { const value = attrs[attr] const isBooleanAttribute = value === '' || String(value).toLowerCase() === attributeName.toLowerCase(); result[name] = isBooleanAttribute ? true : value; } return result; }从代码分析:通过正则 /^on.*/.test(attr) 判断是否内联事件,如果是则忽略掉(所有内联事件都不会生效)。转化除了 data-和aria- 并且不在preserveAttributes 数组内的属性成驼峰式。把html属性转化为符合React规范的属性,具体如何转化的下面提供了一个JSON文件:{ "for": "htmlFor", "class": "className", "acceptcharset": "acceptCharset", "accesskey": "accessKey", "allowfullscreen": "allowFullScreen", "autocomplete": "autoComplete", "autofocus": "autoFocus", "autoplay": "autoPlay", "cellpadding": "cellPadding", "cellspacing": "cellSpacing", "charset": "charSet", "classid": "classID", "classname": "className", "colspan": "colSpan", "contenteditable": "contentEditable", "contextmenu": "contextMenu", "crossorigin": "crossOrigin", "datetime": "dateTime", "enctype": "encType", "formaction": "formAction", "formenctype": "formEncType", "formmethod": "formMethod", "formnovalidate": "formNoValidate", "formtarget": "formTarget", "frameborder": "frameBorder", "hreflang": "hrefLang", "htmlfor": "htmlFor", "httpequiv": "httpEquiv", "inputmode": "inputMode", "keyparams": "keyParams", "keytype": "keyType", "marginheight": "marginHeight", "marginwidth": "marginWidth", "maxlength": "maxLength", "mediagroup": "mediaGroup", "minlength": "minLength", "novalidate": "noValidate", "radiogroup": "radioGroup", "readonly": "readOnly", "rowspan": "rowSpan", "spellcheck": "spellCheck", "srcdoc": "srcDoc", "srclang": "srcLang", "srcset": "srcSet", "tabindex": "tabIndex", "usemap": "useMap", "viewbox": "viewBox" }转行内样式成StyleObject;转化布尔属性什么是布尔属性❓总结htmr内部对html字符串进行dom转换,接着递归遍历所有节点,对节点(和属性)过滤、转换,再通过React.createElement()把字符串转成React组件。作者:路一直在_走链接:https://www.jianshu.com/p/36639da716e7来源:简书著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
2021年09月11日
317 阅读
0 评论
0 点赞
2021-08-28
警告:React Hook useEffect has a missing dependency
问题在使用useEffect时,当我们将函数的声明放在useEffect函数外面时,会报eslint警告webpackHotDevClient.js:119 ./src/pages/detail/enterprise/modules/businessWarning/modules/tendAndBid/modules/topTitle/index.jsx Line 27:6: React Hook useEffect has a missing dependency: 'menuConfig'. Either include it or remove the dependency array. You can also do a functional update 'setMenuConfig(m => ...)' if you only need 'menuConfig' in the 'setMenuConfig' call react-hooks/exhaustive-deps解决办法1.使用 // eslint-disable-line react-hooks/exhaustive-depsuseEffect(() => { clickshow() }, [showConfirmation])// eslint-disable-line react-hooks/exhaustive-deps function clickshow() { if (showConfirmation) { document.addEventListener('click', isShowConfir, true); } else { document.removeEventListener('click', isShowConfir, true); } } function isShowConfir(e) { if (showConfirmation) { let _con = confDom.current; if (_con && !_con.contains(e.target)) { e.stopPropagation(); setShowConfirmation(false) } } }2.将函数 放在useEffect内useEffect(() => { function clickshow() { if (showConfirmation) { document.addEventListener('click', isShowConfir, true); } else { document.removeEventListener('click', isShowConfir, true); } } function isShowConfir(e) { if (showConfirmation) { let _con = confDom.current; if (_con && !_con.contains(e.target)) { e.stopPropagation(); setShowConfirmation(false) } } } clickshow() }, [showConfirmation])总结以上两种方式可以解决useEffect警告,类似useCallback等警告也可以通过方法1解决。
2021年08月28日
267 阅读
0 评论
0 点赞
1
2