作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
Kamil是一个React Native, JavaScript和 .最近一直专注于React Native的。NET粉丝. 他在招聘方面也很有经验.
10
正如文章中所指出的 这是React教程的第一部分在美国,入门React相对容易. 首先使用Create React App (CRA),初始化一个新项目,然后开始开发. Sadly, over time, 您可能会遇到这样的情况:您的代码将变得相当难以维护, 特别是如果你是React的新手. 组件可能会变得不必要地大,或者您可能最终得到可能是组件但不是组件的元素, 所以你可以在这里和那里写重复的代码.
这就是你应该尝试真正开始你的React之旅的地方——开始思考 React开发解决方案.
每当你接触一个新的应用程序, 一个新的设计,你需要稍后转换成一个React应用程序, 首先试着决定你的草图中会有哪些组件, 如何将草图分开,使它们更容易管理, 哪些元素是重复的(或它们的行为), at least). 尽量避免添加可能“在将来有用”的代码——这可能很诱人, 但是,这种未来可能永远不会到来,您将保留具有大量可配置选项的额外通用函数/组件.
Also, 如果组件长度大于, 比方说2-3个窗高, 也许它值得分开(如果可能的话)——因为这样以后更容易阅读.
在大多数应用中, 需要输入和与用户进行某种形式的交互, 允许他们输入一些东西, 上传文件, 选择一个字段, and so on. React以两种不同的方式处理用户交互controlled and uncontrolled components.
被控制组件的值, 顾名思义, 是由React通过向与用户交互的元素提供一个值来控制的, 而非受控元素则没有value属性. 多亏了这个, 我们只有一个真实的来源,那就是React状态, 因此,我们在屏幕上看到的和我们目前所处的状态之间并不存在不匹配. 开发人员需要传递一个函数,该函数将响应用户与表单的交互, 哪个会改变它的状态.
类ControlledInput扩展React.Component {
state = {
value: ""
};
onChange = (e) => this.setState({value:值.target.value });
render() {
return (
);
}
}
在不受控制的React组件中, 我们不关心值如何变化, 但是如果我们想知道确切的值, 我们只是通过ref访问它.
UncontrolledInput类扩展React.Component {
input = React.createRef();
getValue = () => {
console.log(this.input.current.value);
};
render() {
return (
);
}
}
那么什么时候应该用? 我想说,在大多数情况下,控制组件是可行的, 但也有一些例外. 例如,需要在React中使用非受控组件的一种情况是 file
type input, 因为它的值是只读的,不能通过编程方式设置(需要用户交互)。. 此外,我发现受控组件更容易阅读和更容易使用. 对受控组件进行验证是基于呈现的, 状态可以改变, 我们可以很容易地指出输入(e)有问题.g.,格式或为空).
我们已经提到过 refs
,这是在类组件中可用的特殊功能,直到16年出现钩子.8.
ref可以让开发人员通过引用访问React组件或DOM元素(取决于我们附加ref的类型). 最好的做法是尽量避免使用它们,并且只在必须的场景中使用它们, 因为它们使代码更难阅读,并且破坏了从上到下的数据流. 然而,在某些情况下,它们是必要的,特别是在DOM元素上(例如.g.,以编程方式改变焦点). 当附加到React组件元素时, 您可以在所引用的组件中自由地使用方法. 尽管如此,这种做法应该避免,因为有更好的方法来处理它.g.(提升状态并将函数移动到父组件).
Refs也有三种不同的实现方式:
React.createRef()
并将其绑定到类属性并通过它访问它(注意,引用将从componentDidMount生命周期中可用).Finally, 在某些情况下,引用没有向下传递,并且当您希望从当前组件访问更深层次的引用元素时(例如.g., you have a 组件的内部
DOM元素,现在你在
组件,并且您希望从行组件中访问输入DOM焦点函数. 这就是你要用的地方 forwardRef
).
引用没有被传递的一种情况是 高阶分量 在组件上使用——原因是可以理解的 ref
is NOT a prop
(similar to key
),所以它不会被传递下去,所以它会引用 HOC
而不是组件被它包装. 在这种情况下,我们可以用 React.forwardRef
它将props和refs作为参数,然后可以赋值给 prop
然后传递给我们想要访问的组件.
函数withNewReference(组件){
类Hoc扩展React.Component {
render() {
const {forwardedRef, ...props} = this.props;
return ;
}
}
return React.forwardRef((props, ref) => {
return ;
});
}
事情越复杂,出错的可能性就越大. That’s why 错误的界限 是React的一部分吗. 那么它们是如何工作的呢?
如果出了问题而父节点没有错误边界, 它会导致整个React应用失败. 与其误导用户,显示错误信息,不如不显示信息, 但这并不一定意味着你应该崩溃整个应用程序并显示一个白屏. 有了错误边界,您可以使用更多的灵活性. 你可以在整个应用中使用一个,然后显示一条错误信息, 或者在某些小部件中使用它而不显示它们, 或者在这些小部件的位置显示少量信息.
请记住,它只涉及声明性代码的问题,而不是为处理某些事件或调用而编写的命令式代码. 对于这些,你仍然应该使用常规的 try/catch approach.
错误边界也是您可以将信息发送到 Error Logger 你用的那个 componentDidCatch
生命周期方法).
类ErrorBoundary扩展React.Component {
state = {hasError: false};
getDerivedStateFromError(error) {
return {hasError: true};
}
componentDidCatch(error, info) {
logToErrorLogger(错误,信息);
}
render() {
if (this.state.hasError) {
return Help, something went wrong.;
}
return this.props.children;
}
}
高阶分量(HOC) 在React中经常被提及,并且是非常重要的 受欢迎的模式,您可能会使用(或已经使用过). 如果你熟悉hoc,你可能见过 withNavigation, connect, withouter
在许多图书馆.
HOC只是将组件作为参数的函数,与没有使用HOC包装器的组件相比,它将返回一个具有扩展功能的新组件. 多亏了这个, 您可以实现一些易于扩展的功能,这些功能可以增强您的组件.g.,导航通道). hoc也可以采取几种形式,这取决于我们有什么, 唯一需要的参数总是一个组件, 但是它可以接受额外的参数——一些选项, or like in connect
, 首先调用一个带有配置的函数,然后返回一个接受参数组件并返回HOC的函数.
以下是一些你可以添加但应该避免的内容:
React.forwardRef
如前所述,解决这类问题.导出函数importantHoc() {
return (Component) => class extends React.Component {
importantFunction = () => {
console.log("非常重要的功能");
};
render() {
return (
);
}
};
}
样式化不一定与React本身相关,但有很多原因值得提及.
First of all, 常规的CSS/内联样式在这里正常应用,你可以简单地在className属性中添加CSS中的类名, 它会正常工作的. 内联样式与常规HTML样式略有不同. 字符串不是通过样式传递下来的,而是每个对象都有正确的值. 样式属性也是camelcase,所以border-radius变成borderRadius,等等.
React似乎推广了一些解决方案,这些解决方案不仅在React中很常见, 例如最近集成在CRA中的CSS模块, 在哪里可以简单地导入 name.modules.css
并使用它的类(如属性)来样式化您的组件(某些ide,例如.g., WebStorm,也有自动完成,它告诉你什么名字是可用的).
另一个在React中也很流行的解决方案是CSS-in-JS.g., the emotion
library). 再次指出,CSS模块和情感(或一般的CSS-in- js)是 not limited to React.
Hooks 很可能是React自重写以来最期待的新功能. 这个产品真的像宣传的那样好吗? 从我的角度来看,是的,因为它们确实是一个很棒的功能. 它们本质上是开辟新机会的职能,例如:
class
我们用这些分量是因为我们不能有e.g.例如,局部状态或ref,因此组件的代码看起来更容易阅读.setState
from useState
in useEffect
).有几个React钩子是默认包含的. 三个基本的是 useState
, useEffect
, and useContext
. 还有几个额外的,e.g., useRef
and useMemo
,但现在,我们将重点关注基础知识.
让我们一起来看看 useState
,让我们用它来创建一个简单的计数器示例. 它是如何工作的?? 基本上,整个结构非常简单,看起来像:
导出函数Counter() {
const [counter, setCounter] = React.useState(0);
return (
{counter}
);
};
调用它的方法是 initialState
(value)并返回一个包含两个元素的数组. 由于数组解构赋值,我们可以立即将变量赋值给这些元素. 第一个总是更新后的最后一个状态, 而另一个是一个函数,我们将使用它来更新值. 看起来很简单,不是吗?
Also, 因为这样的组件过去被称为无状态功能组件, 这样的名字已经不合适了, 因为它们可以有一个如上所示的状态. 因此,这些名字 类组件 and 功能组件 似乎更符合他们的实际行为,至少从16岁开始.8.0.
更新函数(在本例中为 setCounter
), 也可以作为一个函数使用,该函数将以以下形式将前一个值作为参数:
然而,与 this.setState
类组件正在进行浅合并,将函数(setCounter
在我们的例子中)重写了整个状态.
In addition, initialState
也可以是一个函数,而不仅仅是一个普通的值. 这有它自己的好处, 因为该函数只会在组件的初始呈现期间和之后运行, 它将不再被调用.
const [counter, setCounter] = useState(() => calculateComplexInitialValue());
最后,如果我们要用 setCounter
与我们在当前状态下的同一时刻拥有的值完全相同(counter
),则组件 will not rerender.
另一方面, useEffect
是在功能组件中添加副作用吗, 无论是订阅, API calls, timers, 或者是任何我们觉得有用的东西. 我们要传递给的任何函数 useEffect
会在渲染后运行吗, 它将在每次渲染后这样做,除非我们添加一个限制,关于什么属性的变化应该作为函数的第二个参数重新运行. 如果我们只想在mount时运行它,在unmount时进行清理, 然后我们只需要向它传递一个空数组.
const fetchApi = async () => {
Const value = await fetch("http://jsonplaceholder . value ").typicode.com/todos/1”);
console.日志(等待的价值.json());
};
导出函数Counter() {
const [counter, setCounter] = useState(0);
useEffect(() => {
fetchApi();
}, []);
return (
{counter}
);
};
上面的代码将只运行一次,因为第二个参数是空数组. 基本上,它是这样的 componentDidMount
在这种情况下,它会晚一点触发. 如果您希望在浏览器绘制之前调用类似的钩子,请使用 useLayoutEffect
,但这些更新将同步应用,不像 useEffect
.
useContext
这似乎是最容易理解的, 方法返回的一个对象提供了我们想要访问的上下文 createContext
函数),作为回报,它为我们提供该上下文的值.
const context = useContext(context);
最后,要编写你自己的钩子,你可以这样写:
函数useWindowWidth() {
let [windowWidth, setWindowWidth] = useState(窗口宽度.innerWidth);
函数handleResize() {
setWindowWidth(窗口.innerWidth);
}
useEffect(() => {
window.addEventListener(“调整”,handleResize);
return () => window.removeEventListener(“调整”,handleResize);
}, []);
返回windowWidth;
}
基本上,我们用的是普通的 useState
我们将其赋值为初始值窗口宽度的钩子. Then in useEffect,
我们正在添加一个将触发的侦听器 handleResize
在每个窗口上调整大小. 我们也清除组件将卸载后(查看返回在 useEffect
). Easy?
Note: The word use 在所有的钩是重要的. 使用它是因为它允许React检查您是否没有做坏事.g.,从常规JS函数调用钩子.
在Flow和TypeScript成为选项之前,React有自己的道具检查功能.
PropTypes检查React组件是否接收到属性(props),并检查它们是否与我们所拥有的一致. 每当不同的情况发生时(e).g.(对象而不是数组),我们将在控制台中得到一个警告. 需要注意的是,由于PropTypes对性能的影响和前面提到的控制台警告,所以只有在开发模式下才会检查PropTypes.
从React 15开始.5、PropTypes在不同的包中,需要单独安装. 它们是与静态属性一起声明的 propTypes
(惊讶),将它们与 defaultProps
如果属性未定义(undefined 是唯一的情况吗?. DefaultProps与PropTypes无关, 但它们可以解决一些可能由于PropTypes而出现的警告.
另外两个选项是Flow和TypeScript, 它们现在更流行了(尤其是TypeScript).
Personally, 我发现TypeScript更快(几乎是即时的), 特别是在自动补全方面, 这在Flow中似乎有点慢. 值得注意的是,WebStorm等ide, 我个人使用的, 使用CLI与Flow集成. 然而,在文件中集成可选使用似乎更容易,只需添加 // @flow
在文件的开头开始类型检查. Also, 据我所知, 看起来TypeScript最终战胜了Flow——它现在更受欢迎了, 一些最流行的库正在从Flow重构到TypeScript.
还有更多的选择, 在官方文件中也有提及, 比如Reason(由Facebook开发,在React社区中越来越受欢迎), Kotlin(一种由JetBrains开发的语言)等等.
Obviously, 前端开发人员, 最简单的方法就是开始使用Flow和TypeScript, 而不是切换到Kotlin或f#. However, 对于正在过渡到前端的后端开发人员, 这些可能更容易上手.
对于生产模式,您需要做的最基本和最明显的更改是切换到“生产”模式 DefinePlugin
and add UglifyJsPlugin
以Webpack为例. 在CRA的情况下,它就像使用一样简单 NPM运行构建
(也就是跑步 react-scripts构建
). 请注意,Webpack和CRA不是唯一的选择,因为您可以使用其他构建工具,如Brunch. 这通常包含在官方文档中, 无论是官方的React文档还是特定工具的文档. 要确保模式设置正确,可以使用 React开发工具,这将给你一个指示,你正在使用哪种类型的构建(生产vs. development). 上述步骤将使您的应用程序在没有来自React的检查和警告的情况下运行,并且bundle本身将被最小化, too.
你还可以为你的React应用做更多的事情. 如何处理构建的JS文件? 你可以从“bundle”开始.如果尺寸相对较小,则为Js, 或者做一些类似于“供应商+包”或者“供应商+最小的必需部分+在需要的时候导入东西”的事情.“当你在处理一个非常大的应用程序时,这很有用,你不需要一开始就导入所有内容. 请注意,在主包中捆绑一些甚至不被使用的JavaScript代码只会增加包的大小,并使应用程序在一开始加载得更慢.
如果你意识到库的版本可能在很长一段时间内(如果有的话)不会改变,而打算冻结这些库的版本,供应商捆绑包可能会很有用。. Also, 更大的文件更适合gzip,所以你从分离中获得的好处有时可能不值得. 这取决于文件大小,有时你只需要自己尝试一下.
代码分割可以以比这里建议的更多的方式出现, 但让我们把重点放在CRA和React本身可用的东西上. 基本上,为了将代码分成不同的块,我们可以使用 import()
这要感谢Webpack (import
到目前为止,它本身只是第三阶段的一个提案,所以它还不是语言标准的一部分)。. 每当Webpack看到 import
, 它将知道它需要在这个阶段开始代码分割,并且不能将其包含在主包中(它在导入中的代码)。.
现在我们可以把它和 React.lazy()
这需要 import()
使用包含需要在该位置呈现的组件的文件路径. 接下来,我们可以用 React.suspense()
在导入的组件被加载之前,哪个组件会在该位置显示不同的组件. One might wonder; if we are importing a single component, then why we would need it?
但事实并非如此 React.lazy()
会显示我们的组件吗 import()
, but import()
可能会获取比单个组件更大的数据块. 例如,该特定组件可能包含其他库、更多代码等.,所以一个文件不是必需的,它可能是捆绑在一起的更多文件. 最后,我们可以把所有这些包起来 ErrorBoundary
(您可以在错误边界一节中找到代码) 如果我们想要导入的组件出现故障(e.g.,如果有网络错误).
import ErrorBoundary from './ ErrorBoundary”;
const ComponentOne = React.lazy(() => import('./ ComponentOne '));
MyComponent() {
return (
Loading...
这是一个基本的例子,但显然你可以做更多. You can use import
and React.lazy
对于动态路由拆分(e.g., admin vs. 普通用户,或者只是真正的大路径,带来很多). 请注意 React.lazy
目前只支持默认导出,不支持服务器端呈现.
关于性能, 如果你的React应用程序运行滞后, 有两个工具可以帮助您解决这个问题.
第一个是 Chrome性能标签,它会告诉你每个分量(e.g., mount, update). 多亏了这个, 您应该能够确定哪个组件出现了性能问题,然后对其进行优化.
另一个选择是使用 DevTools分析器 在React 16中可用.5+,并与shouldComponentUpdate(或PureComponent)合作,这在 本教程的第一部分),我们可以提高某些关键组件的性能.
显然,采用基本的web最佳实践是最优的,例如公开某些事件(例如.g., scroll), 小心使用动画(使用变换而不是改变高度和动画)等等. 使用最佳实践很容易被忽视, 特别是如果你刚刚开始掌握React.
如果我们要讨论React的未来,我个人不会太担心. 在我看来,React在2019年及以后将毫无疑问地保持其宝座.
React拥有如此强大的地位, 由一个庞大的社区支持, 很难推翻他的王位. React社区很棒, 它并没有耗尽创意, 核心团队也在不断改进React, 添加新功能和修复旧问题. React也得到了一家大公司的支持, 但是授权问题已经解决了——现在是麻省理工学院授权的.
Yes, there are a few things that are expected to be changed or improved; for instance, 让React更小一点(其中一个提到的措施是删除合成事件)或重命名 className
to class.
Of course, 即使这些看似微小的变化也可能导致诸如影响浏览器兼容性之类的问题. Personally, 我也想知道当WebComponent变得更受欢迎时会发生什么, 因为它可能会增强React今天经常使用的一些东西. 我不相信他们会完全取代, 但我相信它们可以很好地互补.
至于短期,hook只是出现在React中. 这可能是自React重写以来最大的变化, 因为它们将开辟许多可能性,并进一步增强功能组件(它们现在真的被炒作了)。.
最后,这也是我最近一直在做的事情 React Native. For me, 这是一项伟大的技术,在过去几年中发生了很大的变化(对大多数人来说,缺乏原生响应链接可能是最大的问题), 显然有很多bug). React Native的核心正在被重写, 这应该以类似于React重写的方式完成(都是内部的), 开发者不应该做出任何改变。). 异步渲染,本地和JavaScript之间更快、更轻量的桥梁,等等.
React生态系统中有很多值得期待的东西, 但是hooks(以及React Native,如果有人喜欢手机的话)的更新可能是我们在2019年看到的最重要的变化.
钩子是让你从函数组件中“钩入”React状态和生命周期特性的函数.
它们应该以“use”开头,并且只能从React组件函数中调用. 您也可以创建自己的钩子!
在某些情况下,是的. 上传文件, 它们是必要的, 但大多数情况下, 受控组件更容易使用.
Mostly. First of all, 与分割相关的性能在RN中不存在(它可以完成,但不是开箱-它需要相当黑,因为Metro还不能处理这样的东西). 其他的都适用于RN.
不要使用旧版本. 首选的方法是使用createRef,但是回调也可以.
如果有些事可以用钩子来解决,我就用钩子. 它们更容易使用(至少对我来说),因为它们似乎更容易与不同的组件共享,而且它们不会在渲染道具的情况下带来厄运金字塔,或者在hoc中“这是从哪里来的”.
它们可以解决我们之前使用hoc和渲染道具模式时遇到的一些代码共享问题,这些问题有时会让代码变得有点复杂.
Kamil是一个React Native, JavaScript和 .最近一直专注于React Native的。NET粉丝. 他在招聘方面也很有经验.
10
世界级的文章,每周发一次.
世界级的文章,每周发一次.