错误使用 fminbnd (第 109 行) 边界必须为标量。_应对React面试前,必须要了解的25个话题(下)...

1a7fbe45644bd23d3195dd2c216baa2b.gif

本文原载于SegmentFault专栏"终身学习者"

翻译自 Learn Enough React For The Interview

翻译:前端小智

整理编辑:SegmentFault

React是流行的javascript框架之一,在2019年及以后将会更加流行。React于2013年首次发布,多年来广受欢迎。它是一个声明性的、基于组件的、用于构建用户界面的高效javascript库。

以下是面试前必须了解的话题,今天我们为大家分享其中的下半部分——

  • 什么是声明式编程

  • 声明式编程 vs 命令式编程

  • 什么是函数式编程

  • 什么是组件设计模式

  • React 是什么

  • React 和 Angular 有什么不同

  • 什么是虚拟DOM及其工作原理

  • 什么是JSX

  • 组件和不同类型

  • Props 和 State

  • 什么是 PropTypes

  • 如何更新状态和不更新状态

  • 组件生命周期方法

  • 超越继承的组合

  • 如何在React中应用样式

  • 什么是Redux及其工作原理

  • 什么是React路由器及其工作原理

  • 什么是错误边界

  • 什么是 Fragments

  • 什么是传送门(Portals)

  • 什么是 Context

  • 什么是 Hooks

  • 如何提高性能

  • 如何在重新加载页面时保留数据

  • 如何从React中调用API

  • 总结

相关阅读:

应对React面试前,必须要了解的25个话题(上)

超越继承的组合

在React中,我们总是使用组合而不是继承。我们已经在函数式编程部分讨论了什么是组合。这是一种结合简单的可重用函数来生成高阶组件的技术。下面是一个组合的例子,我们在 dashboard 组件中使用两个小组件todoFormtodoList

import React from 'react';import '../App.css';import { ToDoForm } from './todoform';import { ToDolist } from './todolist';export class Dashboard extends React.Component {  render() {    return (      <div className="dashboard">           <ToDoForm />          <ToDolist />      div>    );  }}

如何在React中应用样式

将样式应用于React组件有三种方法。

外部样式表

在此方法中,你可以将外部样式表导入到组件使用类中。 但是你应该使用className而不是class来为React元素应用样式, 这里有一个例子。

import React from 'react';import './App.css';import { Header } from './header/header';import { Footer } from './footer/footer';import { Dashboard } from './dashboard/dashboard';import { UserDisplay } from './userdisplay';function App() {  return (    <div className="App">      <Header />      <Dashboard />      <UserDisplay />      <Footer />    div>  );}export default App;
内联样式

在这个方法中,我们可以直接将 props 传递给HTML元素,属性为style。这里有一个例子。这里需要注意的重要一点是,我们将javascript对象传递给style,这就是为什么我们使用 backgroundColor 而不是CSS方法backbackground -color

import React from 'react';export const Header = () => {    const heading = 'TODO App'    return(        <div style={{backgroundColor:'orange'}}>            <h1>{heading}h1>        div>    )}
定义样式对象并使用它

因为我们将javascript对象传递给style属性,所以我们可以在组件中定义一个style对象并使用它。下面是一个示例,你也可以将此对象作为 props 传递到组件树中。

import React from 'react';const footerStyle = {    width: '100%',    backgroundColor: 'green',    padding: '50px',    font: '30px',    color: 'white',    fontWeight: 'bold'}export const Footer = () => {    return(        <div style={footerStyle}>            All Rights Reserved 2019        div>    )}

什么是Redux及其工作原理

Redux 是 React的一个状态管理库,它基于flux。 Redux简化了React中的单向数据流。 Redux将状态管理完全从React中抽象出来。

它是如何工作的

在React中,组件连接到 redux ,如果要访问 redux,需要派出一个包含 id和负载(payload) 的 action。action 中的 payload 是可选的,action 将其转发给 Reducer。

reducer收到action时,通过 swithc...case 语法比较 action 中type。 匹配时,更新对应的内容返回新的 state

Redux状态更改时,连接到Redux的组件将接收新的状态作为props。当组件接收到这些props时,它将进入更新阶段并重新渲染 UI。

ab0b0453a7469fdd7b1eeae54a4a0f21.png

Redux 循环细节

让我们详细看看整个redux 循环细节。

1a7d37c96c990bb9eb06bae76ff1d5fb.png

Action: Action 只是一个简单的json对象,type 和有payload作为键。type 是必须要有的,payload是可选的。下面是一个 action 的例子。

// action{   type:"SEND_EMAIL",   payload: data};

Action Creators:这些是创建Actions的函数,因此我们在派发action时不必在组件中手动编写每个 action。 以下是 action creator 的示例。

// action creatorexport function sendEamil(data) {    return { type:"SEND_EMAIL", payload: data};}

Reducers:Reducers 是纯函数,它将 action和当前 state 作为参数,计算必要的逻辑并返回一个新r的state。 这些 Reducers 没有任何副作用。 它不会改变 state 而是总是返回 state 。

export default function emailReducer(state = [], action){   switch(action.type) {      case "SEND_EMAIL":  return Object.assign({}, state, {       email: action.payload      });      default: return state;  }}
组件如何与 redux 进行连接

mapStateToProps:此函数将state映射到 props 上,因此只要state发生变化,新 state 会重新映射到 props。 这是订阅store的方式。

mapDispatchToProps:此函数用于将 action creators 绑定到你的props 。以便我们可以在第12行中使用This . props.actions.sendemail()来派发一个动作。

connectbindActionCreators来自 redux。 前者用于连接 store ,如第22行,后者用于将 action creators 绑定到你的 props ,如第20行。

// import connectimport { connect } from 'react-redux'import { bindActionCreators } from 'redux'// import action creatorsimport * as userActions from '../../../actions/userActions';export class User extends React.Component {      handleSubmit() {        // dispatch an action        this.props.actions.sendEmail(this.state.email);    }  }// you are mapping you state propsconst mapStateToProps = (state, ownProps) => ({user: state.user})// you are binding your action creators to your propsconst mapDispatchToProps = (dispatch) => ({actions: bindActionCreators(userActions, dispatch)})export default connect(mapStateToProps, mapDispatchToProps)(User);

什么是 React Router Dom 及其工作原理

react-router-dom是应用程序中路由的库。 React库中没有路由功能,需要单独安装react-router-dom

react-router-dom 提供两个路由器BrowserRouterHashRoauter。前者基于rul的pathname段,后者基于hash段。

前者:http://127.0.0.1:3000/article/num1 后者:http://127.0.0.1:3000/#/article/num1(不一定是这样,但#是少不了的)
react-router-dom 组件
  • BrowserRouter 和 HashRouter 是路由器。

  • Route 用于路由匹配。

  • Link 组件用于在应用程序中创建链接。 它将在HTML中渲染为锚标记。

  • NavLink是突出显示当前活动链接的特殊链接。

  • Switch 不是必需的,但在组合路由时很有用。

  • Redirect 用于强制路由重定向

下面是组件中的LinkNavLinkRedirect 的例子

// normal link"/gotoA">Home// link which highlights currentlu active route with the given class name"/gotoB" activeClassName=  React// you can redirect to this url"/gotoC" />

以下是 react router 组件的示例。 如果你查看下面的示例,我们将匹配路径并使用SwitchRoute呈现相应的组件。

import React from 'react'// import react router DOM elementsimport { Switch, Route, Redirect } from 'react-router-dom'import ComponentA from '../common/compa'import ComponentB from '../common/compb'import ComponentC from '../common/compc'import ComponentD from '../common/compd'import ComponentE from '../common/compe'const Layout = ({ match }) => {    return(            )}export default Layout

什么是错误边界

在 React 中,我们通常有一个组件树。如果任何一个组件发生错误,它将破坏整个组件树。没有办法捕捉这些错误,我们可以用错误边界优雅地处理这些错误。

错误边界有两个作用

  • 如果发生错误,显示回退UI

  • 记录错误

下面是ErrorBoundary类的一个例子。如果类实现了 getDerivedStateFromErrorcomponentDidCatch 这两个生命周期方法的任何一下,,那么这个类就会成为ErrorBoundary。前者返回{hasError: true}来呈现回退UI,后者用于记录错误。

import React from 'react'export class ErrorBoundary extends React.Component {    constructor(props) {      super(props);      this.state = { hasError: false };    }      static getDerivedStateFromError(error) {      // Update state so the next render will show the fallback UI.      return { hasError: true };    }      componentDidCatch(error, info) {      // You can also log the error to an error reporting service      console.log('Error::::', error);    }      render() {      if (this.state.hasError) {        // You can render any custom fallback UI        return <h1>OOPS!. WE ARE LOOKING INTO IT.h1>;      }        return this.props.children;     }  }

以下是我们如何在其中一个组件中使用ErrorBoundary。使用ErrorBoundary类包裹 ToDoFormToDoList。 如果这些组件中发生任何错误,我们会记录错误并显示回退UI。

import React from 'react';import '../App.css';import { ToDoForm } from './todoform';import { ToDolist } from './todolist';import { ErrorBoundary } from '../errorboundary';export class Dashboard extends React.Component {  render() {    return (      <div className="dashboard">         <ErrorBoundary>          <ToDoForm />          <ToDolist />        ErrorBoundary>      div>    );  }}

什么是 Fragments

在React中,我们需要有一个父元素,同时从组件返回React元素。有时在DOM中添加额外的节点会很烦人。使用 Fragments,我们不需要在DOM中添加额外的节点。我们只需要用 React.Fragment 或才简写 <> 来包裹内容就行了。如下 所示:

 // Without Fragments   return (    <div>       <CompoentA />       <CompoentB />       <CompoentC />    div>)// With Fragments     return (    <React.Fragment>       <CompoentA />       <CompoentB />       <CompoentC />    React.Fragment>  )  // shorthand notation Fragments     return (    <>       <CompoentA />       <CompoentB />       <CompoentC />    >  )

什么是传送门(Portals)

默认情况下,所有子组件都在UI上呈现,具体取决于组件层次结构。Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案。

这里有一个例子。默认情况下,父组件在DOM层次结构中有子组件。

c80701ad1cd6e6feaeabeee29e986eb6.png

我们可以将 children 组件移出parent 组件并将其附加 id 为 someid 的 Dom 节点下。

首先,获取 id 为 someid,我们在constrcutorand中创建一个元素div,将child附加到componentDidMount中的someRoot。 最后,我们在ReactDOM.createPortal(this.props.childen),domnode的帮助下将子节点传递给该特定DOM节点。

首先,先获取 id 为someid DOM元素,接着在构造函数中创建一个元素div,在 componentDidMount方法中将 someRoot放到 div 中 。 最后,通过 ReactDOM.createPortal(this.props.childen), domnode)将 children 传递到对应的节点下。

const someRoot = document.getElementById('someid');class Modal extends React.Component {  constructor(props) {    super(props);    this.el = document.createElement('div');  }  componentDidMount() {    someRoot.appendChild(this.el);  }  componentWillUnmount() {    someRoot.removeChild(this.el);  }  render() {    return ReactDOM.createPortal(      this.props.children,      this.el,    );  }}

fc375af4d4551fbb7fb8f87f0069af36.png

什么是上下文

有时我们必须将props 传递给组件树,即使所有中间组件都不需要这些props 。上下文是一种传递props 的方法,而不用在每一层传递组件树。

什么是 Hooks

Hooks 是React版本16.8中的新功能。 请记住,我们不能在函数组件中使用state ,因为它们不是类组件。Hooks 让我们在函数组件中可以使用state 和其他功能。

目前没有重大变化,我们不必放弃类组件。

Hook 不会影响你对 React 概念的理解。 恰恰相反,Hook 为已知的 React 概念提供了更直接的 API:props, state,context,refs 以及生命周期。稍后我们将看到,Hook 还提供了一种更强大的方式来组合他们。

我们可以使用一些钩子,例如useState,useEffect,useContext,useReducer等。

下面是 Hooks 的基本规则

  • Hooks 应该在外层使用,不应该在循环,条件或嵌套函数中使用

  • Hooks 应该只在函数组件中使用。

让我们看一个例子来理解 hooks。 这是一个函数组件,它采用props并在UI上显示这些props。 在useState钩子的帮助下,我们将这个函数组件转换为有状态组件。 首先,我们在第5行定义状态,这相当于

constructor(props) { super(props); this.state = {     name:'myname', age:10, address:'0000 one street' }}

useState返回两个项,一个是user,另一个是setUser函数。 user 是一个可以在没有 this关键字的情况下直接使用的对象,setUser是一个可以用来设置用户点击第21行按钮的状态的函数,该函数等效于以下内容。

this.setState({name:'name changed'})
    1  import React, { useState } from 'react';    2    3  export const UserDisplay = ({name, address, age}) => {    4    5    const [user, setUser] = useState({ name: 'myname', age: 10, address: '0000 onestreet' });    6    7    return (    8        <>    9            <div>    10                <div class="label">Name:div>    11                <div>{user.name}div>    12            div>    13            <div>    14                <div class="label">Address:div>    15                <div>{user.address}div>    16            div>    17            <div>    18              <div class="label">Age:div>    19                <div>{user.age}div>    20            div>    21            <button onClick={() => setUser({name: 'name changed'})}>    22                Click me    23            button>    24        >    25    )    26 }

如何提高性能

我们可以通过多种方式提高应用性能,以下这些比较重要:

  • 适当地使用shouldComponentUpdate生命周期方法。 它避免了子组件的不必要的渲染。 如果树中有100个组件,则不重新渲染整个组件树来提高应用程序性能。

  • 使用create-react-app来构建项目,这会创建整个项目结构,并进行大量优化。

  • 不可变性是提高性能的关键。不要对数据进行修改,而是始终在现有集合的基础上创建新的集合,以保持尽可能少的复制,从而提高性能。

  • 在显示列表或表格时始终使用 Keys,这会让 React 的更新速度更快

  • 代码分离是将代码插入到单独的文件中,只加载模块或部分所需的文件的技术。

如何在重新加载页面时保留数据

单页应用程序首先在DOM中加载index.html,然后在用户浏览页面时加载内容,或者从同一index.html中的后端API获取任何数据。

如果通过点击浏览器中的重新加载按钮重新加载页面index.html,整个React应用程序将重新加载,我们将丢失应用程序的状态。 如何保留应用状态?

每当重新加载应用程序时,我们使用浏览器localstorage来保存应用程序的状态。我们将整个存储数据保存在localstorage中,每当有页面刷新或重新加载时,我们从localstorage加载状态。

3de3680c4827ce18ae8089c6f0e009ff.png

如何在React进行API调用

我们使用redux-thunk在React中调用API。因为reduce是纯函数,所以没有副作用,比如调用API。

因此,我们必须使用redux-thunk从 action creators 那里进行 API 调用。Action creator 派发一个action,将来自API的数据放入action 的 payload 中。Reducers 接收我们在上面的redux循环中讨论的数据,其余的过程也是相同的。

b0a66a9a4f57424770a8462a41ff19bb.png

redux-thunk是一个中间件。一旦它被引入到项目中,每次派发一个action时,都会通过thunk传递。如果它是一个函数,它只是等待函数处理并返回响应。如果它不是一个函数,它只是正常处理。

这里有一个例子。sendEmailAPI是从组件中调用的函数,它接受一个数据并返回一个函数,其中dispatch作为参数。我们使用redux-thunk调用API apiservice,并等待收到响应。一旦接收到响应,我们就使用payload 派发一个action 。

import apiservice from '../services/apiservice';export function sendEmail(data) {    return { type:"SEND_EMAIL", payload: data };}export function sendEmailAPI(email) {    return function(dispatch) {        return apiservice.callAPI(email).then(data => {            dispatch(sendEmail(data));        });    }}

总结

要想有把握的面试,必须充分了解上述所有主题。 即使你目前正在使用React,理解这些概念也能增强你在职场中信心。


欢迎关注 SegmentFault 微信公众号 :)

746da67c9728030f67b8a52ed1c9d2a1.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值