React的10种有效的设计模式
设计React的10种模式
在过去的几年里,我已经做了许多看起来挺不错的React项目。 什么是模式? 这些模式是我想在学习React第一天就知道的事情。 或者你并不幸运。只有一种方法可以决定你是否是幸运的... 这是一个长长的列表,所以你可以跳过无聊的一些模式, 比如:3,6,8,10。 1. Sending data down and up我建议大家新学习React的一件事是传递信息的模式(信息可以是对象,字符串等)和传递方法下来允许子组件传递信息给父组件。 就像把一包芯片和一个对讲机送到地下被困的矿工一样。 图片怎么样?
父组件在左边,子组件在右边。 被称为 这不是一个真正的模式。剩下的肯定都是模式。我承诺。 2. Fixing HTML’s inputsReact和web组件的一个伟大的事情是,如果在html中的东西不能按你想要的方式工作,你可以解决它。 如果你考虑允许用户输入的不同元素,你很快就会看到这些元素的命名是荒谬的,几乎是鲁莽的。 如果我建立一个将有很多用户输入的网站,我做的第一件事之一是解决这个问题。
还有更多的改进:
也许你的应用程序有一个单一的 关键是不要像我这样做。 3. Binding labels to inputs with unique IDs关于输入...如果你关心你的用户,你将通过id / for组合将 但是你不想为你定义的每个输入想出一些聪明和独特的id,谁有时间呢?我不知道你,但我有山羊的视频观看。 (提示:如果您的航班上有一个尖叫的孩子,闭上眼睛,假装您在YouTube上观看的山羊听起来像人类的视频,烦人的声音就会变得很热闹。) 您可以为每个输入/标签对生成随机ID,但是客户端呈现的HTML将与您呈现的服务器呈现的HTML不匹配。这并不是一个好的解决方案 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 ( <label htmlFor={this.id}> {this.props.label} <input id={this.id} value={this.props.value} onChange={this.onChange} /> </label> ); } } 如果 你可以在你的应用程序的入口点,使用一个简单的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的二进制属性一样,你不需要做 设置值在某些情况下,您可能希望直接传递CSS属性的值(在组件中将其设置为内联样式)。
一个例子假设您正在创建链接组件。你通过你的网站的设计和工作,有三个不同的主题,有时他们有一个下划线,有时他们不。
下面是我将如何设计该组件: const Link = (props) => { let className = `link link--${props.theme}-theme`; if (!props.underline) className += ' link--no-underline'; return <a href={props.href} className={className}>{props.children}</a>; }; Link.propTypes = { theme: PropTypes.oneOf([ 'default',// primary color,no underline 'blend',// inherit surrounding styles 'primary-button',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切换组件是呈现最多的组件之一。 这可能是一个显示多个页面之一的<Page>组件。或选项卡集中的选项卡,或模态组件中的不同模态。 我曾经使用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 <Handler {...props} /> }; Page.propTypes = { page: PropTypes.oneOf(Object.keys(PAGES)).isRequired,}; PAGES对象的key可以在prop类型中使用,以捕获dev时间错误。 然后,我们当然会使用这样<page page =“home”/>。 如果你用key替换home,about和user分别用/, /about和/user,你差不多就是个路由器了。 (未来的想法:再见 react-router。) 6. Reaching into a component如果您正在寻找一个简单的方法来请求您的用户输入信息,那么你可以添加自动对焦到输入组件, 当用户一个页面的时候。这种设计仅仅适用于登陆操作就在主页面内执行, 而不是单独弹出一个模态窗口。 你可以通过给输入组件一个id,然后使用 这工作,但不是正确的方式。在你的应用程序中依靠两个字符串匹配的事情越少越好。 幸运的是,有一个非常容易的方法来做到这一点“正确”: class Input extends Component { focus() { this.el.focus(); } render() { return ( <input ref={el=> { this.el = el; }} /> ); } } 真是酷炫屌炸天! 一个具有focus()方法的输入组件,用于聚焦HTML元素。 在父组件中,我们可以获得对Input组件的引用并调用其focus()方法。 class SignInModal extends Component { componentDidMount() { this.InputComponent.focus(); } render() { return ( <div> <label>User name: </label> <Input ref={comp => { this.InputComponent = comp; }} /> </div> ) } } 注意,当在组件上使用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 => ( <li key={listItem.id}>{listItem.name} {listItem.id}</li> ); return ( <ul> {props.listItems.map(renderSearchSuggestion)} </ul> ); } 如果事情变得更复杂,或者您想在其他地方使用此组件,则应该能够将代码复制/粘贴到新组件中。 不要过早组件化。组件不像茶匙;你可以有太多。(意思组件可以随便复制, 但是茶匙不行) 8. Components for formatting text当我第一次开始使用React时,我想到组件应该是一个大东西,一种分组DOM的结构块的方法。但这样组件表现的很一般。 这里是一个<Price>组件,它接受一个数字,并返回一个漂亮的字符串,有或没有小数和一个'$'符号。 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 <span className={props.className}>{price}</span> }; 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 ( <div> <p>One lamb is <Price className="expensive">{lambPrice}</Price></p> <p>One jet is <Price showDecimals={false}>{jetPrice}</Price></p> <p>Those gumboots will set ya back <Price showDecimals={false} showSymbol={false}>{bootPrice}</Price> bucks.</p> </div> ); }; 正如你可以看到,我使用强大的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 ( <div> <p>One lamb is <span className="expensive">{numberToPrice(lambPrice)}</span></p> <p>One jet is {numberToPrice(jetPrice,{ showDecimals: false })}</p> <p>Those gumboots will set ya back {numberToPrice(bootPrice,{ showDecimals: false,showSymbol: false })} bucks.</p> </div> ); }; 请注意,我不会检查我在上述任何一个有效的数字。那是因为 … 9. The store is the component’s servant我可能写了这么几千次: if (props.user.signInStatus === SIGN_IN_STATUSES.SIGNED_IN).. (我被告知,我夸张,像,一个gazillion时代。) 最近我决定,如果我做这样的检查,我做错了。我想只问“是用户登录?”,而不是“用户的登录状态等于登录?” 的组件在他们的生命周期中所做的已经足够, 他们不应该去担心他们的父组件会传一些什么参数。 比如说Price不用管传入的数据是否是数字。 你会看到,如果你的store中的数据被设计为与您的组件匹配,您的组件将更加简单。我之前说过,复杂性是bug隐藏的地方。组件中的复杂性越低,bug出现的几率越低。 但是复杂这个问题肯定存在。 我的建议是:
对于这最后一点,我建议一个单一的模块,所有的按传入的信息重命名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'; 理论上你可以:
但是当我写的代码我来认识到这是一个坏主意,有三个原因:当我写代码的时候, 我才认识到上面的模式并不好,原因有三个。
Wrap up这就是全部, 我非常确定我将在今年看到这些模式的应用。 或许你们今天就会使用它。 你也可以分享一些你觉得不错的模式。 喔, 我决定我不关心你是否点击了绿色的心。
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |