摘要:设计的种模式本文翻译自。剩下的肯定都是模式。真实的事实的特异性是网络开发人员死亡的第一原因。这种设计仅仅适用于登陆操作就在主页面内执行,而不是多带带弹出一个模态窗口。这可以正常的工作,但确不是最好的方式。
设计React的10种模式
本文翻译自10 React mini-patterns。这篇文章由mrcode翻译, 如果哪里翻译的不恰当或有错误的地方,欢迎指出。 同时也希望大家关注我的博客。 关注我的账号。
在过去的几年里,我已经做了许多看起来挺不错的React项目。
在这个神奇的旅程中,一些模式出现过很多次,我发现我一次又一次地重复着这些模式。
什么是模式?
这些模式是我想在学习React第一天就知道的事情。
所以如果今天是你第一天学习React,你是如此的幸运。
或者你并不幸运。只有一种方法可以决定你是否是幸运的...
这是一个长长的列表,所以你可以跳过无聊的一些模式, 比如:3,6,8,10。
1. Sending data down and up我建议大家新学习React的一件事是传递信息的模式(信息可以是对象,字符串等)和传递方法下来允许子组件传递信息给父组件。
就像把一包芯片和一个对讲机送到地下被困的矿工一样。
图片怎么样?
下面的事情是这种模式的最简单的形式。
父组件在左边,子组件在右边。
你可以认为连接这些组件的两个props允许信息在两者之间的任一方向上流动。
被称为items将被传递给子组件, deleteItem将提供给子组件一种方案来发送信息给父组件。
这不是一个真正的模式。剩下的肯定都是模式。我承诺。
2. Fixing HTML’s inputsReact和web组件的一个伟大的事情是,如果在html中的东西不能按你想要的方式工作,你可以解决它。
如果你考虑允许用户输入的不同元素,你很快就会看到这些元素的命名是荒谬的,几乎是鲁莽的。
如果我建立一个将有很多用户输入的网站,我做的第一件事之一是解决这个问题。
还有更多的改进:
输入应该通过onChange方法返回一个值,而不是一个JavaScript事件实例,对不?
你可以进一步确保在onChange返回的数据类型和传递的数据类型相匹配。如果typeof props.value是number,然后将e.target.value回到一个数字,然后再次发出数据。
一组单选按钮在功能上与一样
它是搞砸了,以一种完全不同的方式来对待它们,唯一的区别是UI。
也许你的应用程序有一个单一的
关键是不要像我这样做。
关键是要使它们成为你自己的 - 你不需要继续使用HTML的用户输入元素的坑爹性质。
关于输入...如果你关心你的用户,你将通过id / for组合将元素绑定到你的。
但是你不想为你定义的每个输入想出一些聪明和独特的id,谁有时间呢?我不知道你,但我有山羊的视频观看。
(提示:如果您的航班上有一个尖叫的孩子,闭上眼睛,假装您在YouTube上观看的山羊听起来像人类的视频,烦人的声音就会变得很热闹。)
您可以为每个输入/标签对生成随机ID,但是客户端呈现的HTML将与您呈现的服务器呈现的HTML不匹配。这并不是一个好的解决方案
所以,你可以创建一个小的模块,给出一个递增的ID,并在输入组件中使用它,像这样:
class Input extends React.Component { constructor(props) { super(props); this.id = getNextId(); this.onChange = this.onChange.bind(this); } onChange(e) { this.props.onChange(e.target.value); } render() { return ( ); } }
如果getNextId()每次只是增加一个数字,然后在服务器上渲染时,这个数字会继续上升和起来,最终达到无穷大。因此,您需要在每次呈现应用程序时重置该数字(对于每个网络请求)。
你可以在你的应用程序的入口点,使用一个简单的resetId()或任何你认为最好的名称。
考虑到所有这些,你的超级幻想模块可能看起来像这样:
let count = 1; export const resetId = () => { count = 1; } export const getNextId = () => { return `element-id-${count++}`; }4. Controlling CSS with props
当你想在不同的实例(例如"primary"和"secondary"按钮)应用不同的CSS,你可以传递道具来控制要应用的CSS。
这看起来超级简单的表面,但让我向你保证有很多错误的方法来做到这一点(我已经尝试过他们!)。
有 - 我估计 - 三种不同的方式,你可以控制应用于组件的CSS。
使用标志也许你的一些按钮有圆角,但这不直接对应于您定义的主题。
在这种情况下,你可以坐下你的设计师,并有一致性谈话,或创建一个布尔的道具,可能看起来像这样:
就像HTML的二进制属性一样,你不需要做round = {true}。
设置值在某些情况下,您可能希望直接传递CSS属性的值(在组件中将其设置为内联样式)。
假设您正在创建链接组件。你通过你的网站的设计和工作,有三个不同的主题,有时他们有一个下划线,有时他们不。
下面是我将如何设计该组件:
const Link = (props) => { let className = `link link--${props.theme}-theme`; if (!props.underline) className += " link--no-underline"; return {props.children}; }; Link.propTypes = { theme: PropTypes.oneOf([ "default", // primary color, no underline "blend", // inherit surrounding styles "primary-button", // primary color, solid block ]), underline: PropTypes.bool, href: PropTypes.string.isRequired, children: PropTypes.oneOfType([ PropTypes.element, PropTypes.array, PropTypes.string, ]).isRequired, }; Link.defaultProps = { theme: "default", underline: false, };
增加CSS...
.link--default-theme, .link--blend-theme:hover { color: #D84315; } .link--blend-theme { color: inherit; } .link--default-theme:hover, .link--blend-theme:hover { text-decoration: underline; } .link--primary-button-theme { display: inline-block; padding: 12px 25px; font-size: 18px; background: #D84315; color: white; } .link--no-underline { text-decoration: none; }
你可能已经注意到链接 - 无下划线的选择器是没必要存在的, 因为他双重否定了。
故事时间:我曾经认为写CSS更少的CSS是目标,但它不是。我宁愿有一些双重否定和多选择器规则集,如果它的意思是样式以一个很好的分层方式应用的话。
我相信我以前说过,但缩放网站最困难的事情是CSS。 JavaScript很容易,但是随意使用CSS使你很遭罪 - 一旦你开始混乱,这是不容易中途修改来解决的。
真实的事实:CSS的特异性是网络开发人员死亡的第一原因。如果你在一台大型计算机上,请查看顶部导航栏中的小通知图标的CSS。
这个通知图标是由很多CSS样式组合在一起的。很复杂。
二十三条规则。
这不包括继承自十一个其他规则的样式。行高多带带被覆盖九次。
如果line-height是一只猫,它现在已经死了。
这不能令人愉快地维护。
有了React,我们可以做得更好。我们可以仔细设计哪些类应用于我们的组件。我们可以删除全局样式和移动它所有在我们的Button.scss。我们可以消除对文件的特异性和顺序的所有依赖。
附注: 我梦想着有一天游览器对于样式没有自己的看法(意思就是所有游览器都变得统一, 完全去IE化-。-)。
5. The switching component切换组件是呈现最多的组件之一。
这可能是一个显示多个页面之一的
我曾经使用switch语句,进一步到实际传入我想要渲染的组件。然后从组件本身导出对组件的引用(命名为exports,然后作为组件上的属性)。
真是一堆可怕的想法!
我现在的方法是使用一个对象传递props给Page组件。
import HomePage from "./HomePage.jsx"; import AboutPage from "./AboutPage.jsx"; import UserPage from "./UserPage.jsx"; import FourOhFourPage from "./FourOhFourPage.jsx"; const PAGES = { home: HomePage, about: AboutPage, user: UserPage, }; const Page = (props) => { const Handler = PAGES[props.page] || FourOhFourPage; return}; Page.propTypes = { page: PropTypes.oneOf(Object.keys(PAGES)).isRequired, };
PAGES对象的key可以在prop类型中使用,以捕获dev时间错误。
然后,我们当然会使用这样
如果你用key替换home,about和user分别用/, /about和/user,你差不多就是个路由器了。
(未来的想法:再见 react-router。)
6. Reaching into a component如果您正在寻找一个简单的方法来请求您的用户输入信息,那么你可以添加自动对焦到输入组件, 当用户一个页面的时候。这种设计仅仅适用于登陆操作就在主页面内执行, 而不是多带带弹出一个模态窗口。
你可以通过给输入组件一个id,然后使用document.getElementById("user-name-input")。focus()来将用户的焦点集中在输入组件上。
这工作,但不是正确的方式。在你的应用程序中依靠两个字符串匹配的事情越少越好。
这可以正常的工作, 但确不是最好的方式。 在你的代码中依靠两个字符串匹配的事情越少越好。
幸运的是,有一个非常容易的方法来做到这一点“正确”:
class Input extends Component { focus() { this.el.focus(); } render() { return ( { this.el = el; }} /> ); } }
真是酷炫屌炸天! 一个具有focus()方法的输入组件,用于聚焦HTML元素。
在父组件中,我们可以获得对Input组件的引用并调用其focus()方法。
class SignInModal extends Component { componentDidMount() { this.InputComponent.focus(); } render() { return ({ this.InputComponent = comp; }} />) } }
注意,当在组件上使用ref时,它是对组件(而不是底层元素)的引用,因此您可以访问其方法。
7. Almost-components假设您正在构建一个组件,以便您可以搜索人员。在您输入时,您会看到一个可能匹配的名称和照片列表。这样的东西。
(我正在寻找政治讽刺,因为我像大家一样,对其他人对政治的看法极为感兴趣。)
当设计此组件时,您可能会想到自己:该列表中的每个项目都是自己的SearchSuggestion组件?它真的只有几行HTML和CSS,也许不是?但我曾经被告知“如果有疑问,创造另一个组件”。
哦,我的,这是相当稀烂的一个泡菜,不是吗?
如果我是做这个,我不会有一个多带带的组件。相反,只是一个renderSearchSuggestion方法返回每个条目的适当的DOM。然后结果就是下面的代码示例这样:
const SearchSuggestions = (props) => { // renderSearchSuggestion() behaves as a pseduo SearchSuggestion component // keep it self contained and it should be easy to extract later if needed const renderSearchSuggestion = listItem => (
如果事情变得更复杂,或者您想在其他地方使用此组件,则应该能够将代码复制/粘贴到新组件中。
不要过早组件化。组件不像茶匙;你可以有太多。(意思组件可以随便复制, 但是茶匙不行)
8. Components for formatting text当我第一次开始使用React时,我想到组件应该是一个大东西,一种分组DOM的结构块的方法。但这样组件表现的很一般。
这里是一个
const Price = (props) => { const price = props.children.toLocaleString("en", { style: props.showSymbol ? "currency" : undefined, currency: props.showSymbol ? "USD" : undefined, maximumFractionDigits: props.showDecimals ? 2 : 0, }); return {price} }; Price.propTypes = { className: React.PropTypes.string, children: React.PropTypes.number, showDecimals: React.PropTypes.bool, showSymbol: React.PropTypes.bool, }; Price.defaultProps = { children: 0, showDecimals: true, showSymbol: true, }; const Page = () => { const lambPrice = 1234.567; const jetPrice = 999999.99; const bootPrice = 34.567; return (); };One lamb is
{lambPrice} One jet is
{jetPrice} Those gumboots will set ya back
{bootPrice} bucks.
正如你可以看到,我使用强大的Intl字符串格式化库,这里有一个链接到他们的网站。
我应该指出(在一些朋克之前),这不是一行代码的保存。你可以很容易地使用函数来做到这一点。 (当然,组件只是具有不同形状括号的函数。)
这是更少的代码,但对我的眼睛,不太好:
function numberToPrice(num, options = {}) { const showSymbol = options.showSymbol !== false; const showDecimals = options.showDecimals !== false; return num.toLocaleString("en", { style: showSymbol ? "currency" : undefined, currency: showSymbol ? "USD" : undefined, maximumFractionDigits: showDecimals ? 2 : 0, }); } const Page = () => { const lambPrice = 1234.567; const jetPrice = 999999.99; const bootPrice = 34.567; return (); };One lamb is {numberToPrice(lambPrice)}
One jet is {numberToPrice(jetPrice, { showDecimals: false })}
Those gumboots will set ya back {numberToPrice(bootPrice, { showDecimals: false, showSymbol: false })} bucks.
请注意,我不会检查我在上述任何一个有效的数字。那是因为 …
9. The store is the component’s servant我可能写了这么几千次:
if (props.user.signInStatus === SIGN_IN_STATUSES.SIGNED_IN)..
(我被告知,我夸张,像,一个gazillion时代。)
最近我决定,如果我做这样的检查,我做错了。我想只问“是用户登录?”,而不是“用户的登录状态等于登录?”
的组件在他们的生命周期中所做的已经足够, 他们不应该去担心他们的父组件会传一些什么参数。 比如说Price不用管传入的数据是否是数字。
你会看到,如果你的store中的数据被设计为与您的组件匹配,您的组件将更加简单。我之前说过,复杂性是bug隐藏的地方。组件中的复杂性越低,bug出现的几率越低。
但是复杂这个问题肯定存在。
我的建议是:
制定你的组件的一般结构和他们需要的数据
设计您的store以支持这些要求
做任何你需要做的输入数据,使其适合store。
对于这最后一点,我建议一个单一的模块,所有的按传入的信息重命名props,将字符串转换为数字,将对象转换为数组,将日期字符串转换为日期对象。
如果你正在进行一个react/redux, 你可以在一个动作创建者中获取搜索结果:
fetch(`/api/search?${queryParams}`) .then(response => response.json()) .then(normalizeSearchResultsApiData) // the do-it-all data massager .then(normalData => { // dispatch normalData to the store here });
你的组件将会感谢你的。
10. Importing components without relative paths不这样做的话后患无穷啊!
import Button from "../../../../Button/Button.jsx"; import Icon from "../../../../Icon/Icon.jsx"; import Footer from "../../Footer/Footer.jsx";
或者你可以这样做
import {Button, Icon, Footer} from "Components";
理论上你可以:
在导出每个组件的地方创建单个index.js
使用Webpack的resolve.alias将组件重定向到该索引文件
但是当我写的代码我来认识到这是一个坏主意,有三个原因:当我写代码的时候, 我才认识到上面的模式并不好,原因有三个。
在Webpack2 似乎改变了原有的API。
eslint将会检测到错误, 由于找不到你引用的组件(因为resolve.alias)。
如果你使用一个好的IDE,它会知道你的组件在哪里。你会得到关于不提供所需props的提示, 也无法通过Command+click 打开文件这个功能。如果你这样做,你的IDE将不再知道在哪里找到该组件,你会失去这些给力的功能。
Wrap up这就是全部, 我非常确定我将在今年看到这些模式的应用。 或许你们今天就会使用它。 你也可以分享一些你觉得不错的模式。
喔, 我决定我不关心你是否点击了绿色的心。
I WILL NOT BE DEFINED BY AN INTERNET METRIC.
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/88146.html
摘要:详情怎样规避地狱作者先介绍什么是地狱,以及在开发过程中怎样去规避地狱,一时爽性能问题火葬场。详情其他亮点汇总开发者大会已于北京时间月日凌晨在美国山景城正式启幕。 【前端】 1. JavaScript 的新数据类型:BigInt BigInt 是 JavaScript 中的一个新的数字基本(primitive)类型,可以用任意精度表示整数。使用 BigInt 可以安全地存储和操作大整数,...
摘要:详情怎样规避地狱作者先介绍什么是地狱,以及在开发过程中怎样去规避地狱,一时爽性能问题火葬场。详情其他亮点汇总开发者大会已于北京时间月日凌晨在美国山景城正式启幕。 【前端】 1. JavaScript 的新数据类型:BigInt BigInt 是 JavaScript 中的一个新的数字基本(primitive)类型,可以用任意精度表示整数。使用 BigInt 可以安全地存储和操作大整数,...
摘要:详情怎样规避地狱作者先介绍什么是地狱,以及在开发过程中怎样去规避地狱,一时爽性能问题火葬场。详情其他亮点汇总开发者大会已于北京时间月日凌晨在美国山景城正式启幕。 【前端】 1. JavaScript 的新数据类型:BigInt BigInt 是 JavaScript 中的一个新的数字基本(primitive)类型,可以用任意精度表示整数。使用 BigInt 可以安全地存储和操作大整数,...
摘要:详情怎样规避地狱作者先介绍什么是地狱,以及在开发过程中怎样去规避地狱,一时爽性能问题火葬场。详情其他亮点汇总开发者大会已于北京时间月日凌晨在美国山景城正式启幕。 【前端】 1. JavaScript 的新数据类型:BigInt BigInt 是 JavaScript 中的一个新的数字基本(primitive)类型,可以用任意精度表示整数。使用 BigInt 可以安全地存储和操作大整数,...
阅读 2815·2021-11-24 09:39
阅读 3946·2021-10-27 14:19
阅读 2020·2021-08-12 13:25
阅读 2321·2019-08-29 17:07
阅读 1095·2019-08-29 13:44
阅读 1016·2019-08-26 12:17
阅读 443·2019-08-23 17:16
阅读 2028·2019-08-23 16:46