1.setState是同步还是异步?
在 React 中,setState
是异步更新状态的。这意味着即使你连续多次调用 setState
,实际 DOM 的更新可能只会执行一次。React 会对多次调用 setState
进行批处理,只执行一次更新将状态写入 DOM。
但是,React 还提供了一种同步更新状态的方法:forceUpdate
。这个方法可以在组件中立即强制重新渲染,无论组件的状态是否已经发生变化,也无论其他组件的状态是否已经改变。但是,建议只在必要时,例如在使用计时器或移动动画等情况下,才使用 forceUpdate
方法,因为它可能会破坏 React 的优化算法,从而导致性能问题。
2.react合成事件是什么?与原生事件的区别
React合成事件是React在DOM上封装的一种事件,它是由React实现的,不同于原生浏览器事件。 React合成事件提供了一种跨浏览器,跨平台的解决方案,使得开发者可以使用一致的事件处理方式,而不必担心不同浏览器之间的兼容性问题。
React合成事件与原生事件的区别如下:
-
事件处理方式不同:React合成事件是使用事件委托的方式,所有事件都被绑定在根节点上,然后通过事件冒泡机制传递到目标节点上执行。而原生事件是直接在目标节点上绑定事件处理函数。
-
同步和异步机制不同:React合成事件采用异步批量更新机制,当有多个事件触发时,React会将它们合并并一次性更新状态,避免了状态的多次更新。而原生事件则是同步触发事件处理函数,每次触发事件都会更新状态。
-
事件对象的属性不同:React合成事件对象是对原生事件对象进行了封装,它的属性和方法与原生事件对象有所不同,例如,React合成事件对象中没有冒泡、阻止默认事件等方法,而是使用自定义的方式来实现。
-
兼容性不同:React合成事件能够保证在不同浏览器和平台上有一致的行为,而原生事件则存在兼容性问题,不同浏览器支持的事件类型和事件属性也不尽相同。
3.什么是虚拟DOM?VirtualDOM及其工作原理
虚拟DOM(Virtual DOM)是一种JavaScript对象表示法,它是真实DOM的一个轻量级的、虚拟的映像。虚拟DOM在操作真实DOM的同时,会生成一颗虚拟的树,称为虚拟DOM树。而操作虚拟DOM树的效率要比直接操作真实DOM树的效率要高很多。
虚拟DOM的工作原理如下:
-
当应用程序的状态发生变化时,Virtual DOM会生成一个新的虚拟DOM树。
-
然后,它会将新生成的虚拟DOM树和旧的虚拟DOM树进行比较,找出差异(也就是需要更新的节点)。
-
Virtual DOM仅更新需要更新的部分,将差异应用于真实DOM树中。
-
最终,浏览器渲染最新的DOM树。
通过使用虚拟DOM,前端开发者可以避免手动操作真实DOM的复杂和低效的过程。Virtual DOM可以提高应用程序的性能和响应性,从而提供更好的用户体验。
4.类组件和函数组件之间的区别是啥?
React中常用的两种组件是类组件和函数组件。它们之间的主要区别如下:
-
语法:类组件是使用类的形式定义的,而函数组件是使用函数的形式定义的。
-
状态管理:类组件支持状态管理,可以使用
state
来保存组件状态。函数组件在React Hooks引入后,也可以使用useState
来管理状态。 -
生命周期:类组件有自己的生命周期方法,可以在组件被创建、更新或销毁时执行特定的代码。函数组件在React Hooks引入后,也可以使用
useEffect
来执行类似生命周期的操作。 -
性能:函数组件通常比类组件更轻量级,因为它们不需要实例化,并且不需要维护实例方法和状态。因此,函数组件的渲染速度更快,占用的内存也更少。
总之,如果需要使用组件状态或生命周期方法,应该使用类组件。如果只需要根据属性来渲染组件,那么使用函数组件会更加简单和高效。在React Hooks引入之后,函数组件的功能已经与类组件相当,因此在新项目中使用函数组件是非常常见的。
5.React 中 refs 干嘛用的?
Refs 在 React 中用于访问 DOM 元素或组件实例。使用 ref 可以获取到渲染的元素或组件实例,并在需要时直接操作其属性或方法,而无需通过父组件进行中间传递。
Refs 可以用于以下情况:
- 管理焦点、选中文本或媒体播放。
- 触发命令式动画。
- 集成第三方 DOM 库。
要使用 ref,需要在组件中创建一个 ref 对象,并将其传递给需要访问的 DOM 元素或组件实例。可以在组件的构造函数中初始化 ref 对象,也可以在 JSX 元素中使用回调函数定义 ref。
使用 ref 需要小心,过度使用 ref 可能会导致代码难以理解、维护和测试。因此,应优先使用 React 组件的 props 和状态来管理组件的行为和状态。
6.state 和 props 区别是啥?
在 React 中,state 和 props 都用于管理组件的数据。但是,它们之间有一些重要的区别:
-
State 是组件内部的可变数据,可以通过 this.setState() 方法进行修改。而 props 则是从父组件传递给子组件的不可变数据,子组件不能直接修改 props。
-
State 只能在组件内部进行修改,而 props 不能在组件内部修改。
-
State 可以在组件的 constructor 中初始化,而 props 必须从父组件传递。
-
State 的变化会触发组件的重新渲染,而 props 的变化也会触发组件的重新渲染,但是只有当 props 发生变化时,才会重新传递给子组件。
总结来说,state 是组件内部维护的可变数据,而 props 则是从父组件传递给子组件的不可变数据,通过它们可以实现组件之间的通信和数据共享。
7.如何创建 refs 与使用场景
Refs 是 React 中用来访问 DOM 节点或组件实例的机制,可以将 refs 像 props 一样传递给 React 组件,但是 refs 是直接访问组件内部的 DOM 元素或组件实例的方式,而不是通过 props 传递数据。你可以使用 createRef 或者回调函数的方式来创建 refs。
使用 createRef 创建 refs
使用 createRef 创建 refs 可以在组件中创建一个 ref 对象,然后将这个 ref 对象赋值给组件的某一个元素或组件,通过这个 ref 对象就可以直接访问到该元素或组件的实例了。
import React, { Component } from 'react';
class MyComponent extends Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
componentDidMount() {
console.log(this.myRef.current);
}
render() {
return <div ref={this.myRef}>Hello World</div>;
}
}
上面的代码中,我们通过 createRef 创建了一个 ref 对象,将其赋值给组件的 div 元素,然后在组件的 componentDidMount 生命周期中可以访问到这个 div 元素的实例。
使用回调函数创建 refs
使用回调函数来创建 refs,可以将 ref 对象作为回调函数的参数传递给组件,通过这个参数就可以访问到该元素或组件的实例了。
import React, { Component } from 'react';
class MyComponent extends Component {
constructor(props) {
super(props);
this.myRef = null;
this.setMyRef = element => {
this.myRef = element;
};
}
componentDidMount() {
console.log(this.myRef);
}
render() {
return <div ref={this.setMyRef}>Hello World</div>;
}
}
上面的代码中,我们将 ref 对象作为 setMyRef 回调函数的参数传递给了组件的 div 元素,通过这个参数就可以访问到这个 div 元素的实例了。
使用场景
使用 refs 可以访问到组件内部的 DOM 元素或组件实例,因此在以下场景中使用 refs 可能会更加方便:
- 获取输入框的值或焦点等状态;
- 通过 DOM 操作来修改元素样式或操作元素的属性;
- 访问组件的实例,直接调用组件的方法或属性。
8.函数组件与类组件生命周期
React中的生命周期方法指的是组件在不同阶段执行的一系列方法。它们被分为三个阶段:挂载、更新和卸载。生命周期方法用于处理特定的任务和操作,并帮助我们优化应用程序的性能。
函数组件和类组件在React中的生命周期方法有一些不同:
函数组件的生命周期:
-
componentDidMount:函数组件中没有componentDidMount方法,但是可以使用useEffect Hook模拟componentDidMount行为。
-
componentDidUpdate:函数组件中没有componentDidUpdate方法,但是可以使用useEffect Hook模拟componentDidUpdate行为。
-
componentWillUnmount:函数组件中没有componentWillUnmount方法,但是可以使用useEffect Hook模拟componentWillUnmount行为。
类组件的生命周期:
-
componentDidMount:组件被挂载后调用,在此方法中进行异步操作或与外部库交互。
-
componentDidUpdate:组件更新后调用,在此方法中进行DOM操作、状态同步或网络请求等操作。
-
componentWillUnmount:组件卸载前调用,在此方法中清除定时器、取消订阅或清除缓存等操作。
此外,还有一些其他的生命周期方法,如shouldComponentUpdate、getSnapshotBeforeUpdate和componentDidCatch。这些方法在函数组件和类组件中都是相同的。
9.受控组件和非受控组件区别是啥?
在React中,受控组件和非受控组件是两种不同的组件处理方式。
受控组件是通过组件的状态来控制用户输入的组件。这意味着组件的状态会与用户的输入同步,可以通过设置组件的状态来控制组件的行为。举例来说,一个<input>
元素可以通过组件状态value
来控制,并且任何对该元素的更改都需要通过更改组件的状态来完成。受控组件的优点是:组件的状态和用户的输入可控,方便管理。
class ControlledComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
inputText: ""
};
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event) {
this.setState({
inputText: event.target.value
});
}
render() {
return (
<input type="text" value={this.state.inputText} onChange={this.handleInputChange} />
);
}
}
非受控组件允许用户输入内容自由地更新DOM。通过非受控组件,我们可以直接从DOM中获取元素的值而无需使用状态值。通常情况下,我们会使用ref
来获取元素的值。相比受控组件,非受控组件的优点是:通常会比较简单,更加直观。
class UncontrolledComponent extends React.Component {
constructor(props) {
super(props);
this.inputRef = React.createRef();
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(event) {
console.log(this.inputRef.current.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<input type="text" ref={this.inputRef} />
<button type="submit">Submit</button>
</form>
);
}
}
10 .什么是 React.createElement ?
React.createElement 是 React 中的一个 API,它用于创建虚拟 DOM 元素。它接收三个参数:一个标签名或 React 组件、一个包含属性的对象、以及可选的子元素,然后返回一个表示虚拟 DOM 元素的 JavaScript 对象。虚拟 DOM 元素可以用于构建 React 组件树。例如,以下代码创建一个 h1 元素:
React.createElement("h1", { className: "title" }, "Hello, world!")
或者使用 JSX 语法:
<h1 className="title">Hello, world!</h1>
11.讲讲什么是 JSX ?
JSX 是一种 JavaScript 语法扩展,它允许在 JavaScript 代码中直接编写 HTML 和 XML 标签,同时也可以用于定义自定义组件。它的出现主要是为了简化在 React 中编写 UI 代码的过程。
在 JSX 中,HTML 和 XML 标签可以被视为 React 元素,因此可以使用 React 提供的 API 和属性对其进行操作和渲染。使用 JSX 的语法,可以更直观地描述 UI 组件的结构和行为。
JSX 代码示例:
import React from 'react';
const element = <h1>Hello, World!</h1>;
function App() {
return (
<div>
{element}
<p>This is a JSX example.</p>
</div>
);
}
在上面的代码中,我们可以看到 JSX 是如何在代码中直接编写 HTML 标签的。同时,我们还可以看到 JSX 与 JavaScript 混合在一起,这使得我们可以在 JSX 中使用 JavaScript 变量,并将其渲染为 UI 组件。
需要注意的是,为了使用 JSX,我们需要使用 Babel 等工具将其编译为常规的 JavaScript 代码。
12.为什么不直接更新 state 呢 ?
更新 state
的方式有很多种,比如使用 setState
、使用 useReducer
、使用 Redux
等等。其中,setState
是 React 官方提供的一种更新 state
的方式,在某些情况下它是非常方便和实用的,但在某些场景下它可能会带来一些问题。
一些常见的问题包括:
-
setState
是异步的,所以在更新之后想要得到最新的state
可能需要使用回调函数或者useEffect
等方法 -
对于大规模的数据更新,连续多次调用
setState
可能会带来性能问题 -
对于复杂的组件状态逻辑,使用
setState
可能会导致代码难以维护和理解
因此,根据具体场景和需求,应该选择合适的方式来更新 state
,而不是一味地选择使用 setState
这种方式。
13.使用 React Hooks 好处是啥?
使用 React Hooks 的好处包括:
-
更加简洁:Hooks 提供了一种更加简洁的方式来管理组件状态和生命周期函数。它们可以让你避免使用类组件和 this 关键字,从而使你的代码更加清晰易读。
-
更加灵活:使用 Hooks 可以更加灵活地处理状态和生命周期,因为它们可以在不同的组件中共享和重用。这意味着你可以更加轻松地构建高度复杂的组件和应用程序。
-
更加可测试:Hooks 可以让你更加容易地编写可测试的组件。你可以在组件外部编写测试代码,并使用 Hooks 来注入测试数据和模拟生命周期函数。
-
更加优化:Hooks 可以让你更加优化你的应用程序,因为它们可以使你更加细粒度地控制状态的更新。这可以减少不必要的重新渲染和提高应用程序的性能。
总之,使用 React Hooks 可以让你更加轻松地构建复杂的组件和应用程序,并提高代码的可读性、可重用性、可测试性和性能。
14.React 中的 useState() 是什么?
useState() 是 React 中一个 hook 函数,用于在 function 组件中实现状态管理。它的作用是:在函数组件中添加状态,并在状态发生变化时,触发组件的重新渲染。useState() 返回一个数组,包含两个元素:当前状态的值和一个用于更新状态的函数。使用 useState() 可以让开发者更方便地管理组件的状态,并避免在组件中使用类组件的生命周期函数等繁琐的操作。
15.如何在 ReactJS 的 Props上应用验证?
在 ReactJS 中,可以使用 PropTypes 库来对组件的 Props 进行验证。PropTypes 库是一个内置库,它允许开发人员在组件定义中指定所需的 Prop 类型,并在 Props 与定义的类型不匹配时发出警告。PropTypes 库提供了许多不同的 Prop 类型,例如字符串、数字、数组、对象等,因此可以轻松地对 Props 进行验证。
以下是一个示例组件,它使用 PropTypes 库对 Props 进行验证:
import React from 'react';
import PropTypes from 'prop-types';
const MyComponent = ({ name, age }) => {
return (
<div>
<p>Name: {name}</p>
<p>Age: {age}</p>
</div>
);
};
MyComponent.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number.isRequired,
};
export default MyComponent;
在上面的示例中,MyComponent 组件接受两个 Props:name 和 age。通过在组件定义中使用 PropTypes 库,可以指定这些 Props 的类型,并通过 isRequired 属性指定它们是必需的。PropTypes.string 指定 name 的类型为字符串,PropTypes.number 指定 age 的类型为数字。
如果向 MyComponent 组件传递了无效的 Props,如一个字符串值作为 age,PropTypes 库将会在控制台输出一个警告消息,以帮助开发人员调试问题。
16.当调用setState时,React render 是如何工作的?
当调用 setState
时,React 会在内部更新组件的状态,并计划重新渲染组件。此时,React 会将新的状态与旧的状态进行比较,确定需要更新的部分,然后再次调用 render
方法重新渲染组件。最终,React 将更新的内容呈现在浏览器中。整个过程是自动完成的,无需手动干预。
17.React中setState的第二个参数作用是什么?
在React中,setState()
是用于更新组件状态的方法。第二个参数是一个可选的回调函数,它会在setState()
完成且组件已经更新后被调用。
具体来说,第二个参数的作用是在setState()
更新组件状态后,确保可以在DOM更新后进行一些额外的操作,例如获取最新的DOM节点或执行其他副作用操作。因为setState()
是异步的,它不会立即更新组件状态,而是会在后续的Reconciliation和Commit阶段中进行更新。因此,使用回调函数可以确保在组件状态已经更新后执行需要在更新后立即进行的操作。
示例代码如下:
this.setState(
{ count: this.state.count + 1 },
() => console.log('count updated: ', this.state.count)
);
以上代码中,第二个参数是一个回调函数,它会在setState()
完成后打印更新后的count值。
18.解释 React 中 render() 的目的。
在 React 中,render() 方法的主要目的是将组件渲染为 Virtual DOM,并将其插入到 DOM 中。在 React 中,每当组件状态发生更改时,都会调用 render() 方法重新渲染组件。这是因为 React 使用 Virtual DOM 进行高效的 DOM 操作,而不是直接操作 DOM。因此,每当组件状态发生更改时,React 将重新计算 Virtual DOM,然后比较先前生成的 Virtual DOM 以查找需要进行的最小更改,最后只更新需要更改的部分。这种方法实现了高效的 DOM 操作,提高了应用程序的性能。
19.React的状态提升是什么?
React的状态提升就是用户对子组件操作,子组件不改变自己的状态,通过自己的props把这个操作改变的数据传递给父组件,改变父组件的状态,从而改变受父组件控制的所有子组件的状态,这也是React单项数据流的特性决定的
20.react 强制刷新
可以使用React的forceUpdate()方法来强制刷新组件。这个方法会导致组件的render()方法被再次调用,从而重新渲染组件。
例如,如果你想在组件中添加一个按钮,点击这个按钮可以强制刷新组件,可以这样实现:
import React, { Component } from 'react';
class MyComponent extends Component {
handleClick = () => {
this.forceUpdate();
}
render() {
return (
<div>
<button onClick={this.handleClick}>强制刷新</button>
{/* ... */}
</div>
);
}
}
在这个例子中,当用户点击按钮时,会调用handleClick
方法,这个方法中调用了forceUpdate()
方法,从而强制刷新组件。
21.路由模式是什么 重定向路由
React路由是一种在React应用程序中使用的客户端路由模式。它允许开发人员在应用程序中定义许多不同的路由,这些路由可以引导用户到应用程序中的不同部分或页面。
React路由有两种模式: hash
和 history
。
在hash模式中,路由信息被存储在URL的hash部分中,也就是 #
符号后面。例如,http://example.com/#/page1
。这种模式的优点是浏览器在收到URL后不会向服务器发送请求,而是在客户端直接处理路由变化。缺点是URL看起来有点奇怪,也可能影响SEO。
在history模式中,路由信息被存储在URL的路径部分中,例如,http://example.com/page1
。这种模式的好处是URL看起来更美观,并且更符合传统的URL结构。缺点是需要在开发服务器上配置,以便正确地处理路由信息。当用户访问应用程序中的不同部分时,浏览器会向服务器发送请求。
重定向路由是一种在React应用程序中使用的路由技术,它允许开发人员将用户从一个URL重定向到另一个URL。这在很多情况下都很有用,例如当用户试图访问一个需要身份验证的页面,但是他们还没有登录时,可以将他们重定向到登录页面。React Router提供了一个Redirect
组件,用于实现重定向路由。
22.怎么实现路由懒加载
React 实现路由懒加载的步骤如下:
-
使用
React.lazy()
方法来实现代码分割和懒加载。该方法接受一个返回组件的 Promise 对象。 -
在
React.lazy()
中引入要懒加载的组件。例如:const AsyncComponent = React.lazy(() => import('./AsyncComponent'));
-
在路由组件中使用
React.lazy()
和Suspense
来包装要懒加载的组件。例如:import React, { lazy, Suspense } from 'react'; import { Switch, Route } from 'react-router-dom'; const Home = lazy(() => import("./Home")); const About = lazy(() => import("./About")); function App() { return ( <div> <Suspense fallback={<div>Loading...</div>}> <Switch> <Route exact path="/" component={Home} /> <Route path="/about" component={About} /> </Switch> </Suspense> </div> ); }
-
在
Suspense
组件中使用fallback
属性来指定在组件加载期间显示的组件或元素。在本例中,我们指定了一个简单的 “Loading…” 文本。
这样,当用户访问一个懒加载的路由时,React 会在需要时异步加载该组件并显示一个加载视图。
23.正向传值方式
React中正向传值一般指父组件向子组件传递数据。以下是几种实现正向传值的方式:
-
通过props传递,将数据作为父组件的一个属性传递给子组件,在子组件中可以通过this.props来获取数据。
-
使用context API,在父组件中创建一个Context对象,并通过Provider组件将数据传递给子组件,在子组件中可以通过Consumer组件来获取数据。
-
使用回调函数,在父组件中定义一个回调函数,将其作为属性传递给子组件,在子组件中通过调用该回调函数来向父组件传递数据。
-
使用Redux等状态管理工具,将应用的状态保存在全局的store中,各个组件之间可以通过dispatch和subscribe等方法来访问和修改应用状态。
24.逆向传值方式
在React中,可以通过props进行父组件向子组件的传值,但是要实现子组件向父组件的传值,则需要通过一些逆向传值的方式。
一种常见的逆向传值方式是通过回调函数的形式。在父组件中定义一个回调函数,然后将这个函数作为props传递给子组件。子组件在需要向父组件传递值的时候,调用这个回调函数,并将要传递的值作为参数传递给它。
代码示例:
父组件:
import React, { useState } from 'react';
import ChildComponent from './ChildComponent';
function ParentComponent() {
const [message, setMessage] = useState('');
const handleMessage = (text) => {
setMessage(text)
}
return (
<div>
<p>{message}</p>
<ChildComponent sendMessage={handleMessage} />
</div>
)
}
export default ParentComponent;
子组件:
import React, { useState } from 'react';
function ChildComponent({ sendMessage }) {
const [text, setText] = useState('');
const handleChange = (e) => {
setText(e.target.value);
}
const handleSend = () => {
sendMessage(text);
}
return (
<div>
<input type="text" value={text} onChange={handleChange} />
<button onClick={handleSend}>Send</button>
</div>
)
}
export default ChildComponent;
在这个例子中,父组件定义了一个状态message
和一个回调函数handleMessage
。回调函数作为props传递给子组件。子组件定义了一个状态text
,一个输入框和一个按钮。输入框的值发生变化时,会更新text
状态。当按钮被点击时,会调用父组件传递过来的回调函数sendMessage
,并将text
作为参数传递给它。
这样在子组件中输入文本并点击按钮后,会向父组件传递文本值,父组件会将传递的文本值更新为message
状态,然后将文本值渲染在页面上。
25.同胞传值方式
在React中,兄弟组件之间的传值需要通过它们共同的父组件进行传递,一种常见的方式是通过将共享的状态(如父组件的状态)提升到它们的共同父组件中,并将这些状态作为prop传递给它们。
还有一种常见的方式是使用React的上下文(Context)API。Context提供了一种在组件之间共享值的方式,不需要显示地通过props进行逐层传递。
代码示例:
import React, { createContext, useContext, useState } from 'react';
// 创建一个Context对象
const MyContext = createContext();
function ParentComponent() {
const [message, setMessage] = useState('');
return (
// 将状态通过Provider传递给子组件
<MyContext.Provider value={{ message, setMessage }}>
<ChildComponent1 />
<ChildComponent2 />
</MyContext.Provider>
)
}
function ChildComponent1() {
// 使用 useContext 获取 Provider 的值
const { message } = useContext(MyContext);
return (
<div>
<h2>ChildComponent1</h2>
<p>{message}</p>
</div>
)
}
function ChildComponent2() {
// 使用 useContext 获取 Provider 的 setValue
const { setMessage } = useContext(MyContext);
const handleClick = () => {
setMessage('Hello, ChildComponent1!');
}
return (
<div>
<h2>ChildComponent2</h2>
<button onClick={handleClick}>Send Message</button>
</div>
)
}
export default ParentComponent;
在这个例子中,我们在父组件中创建了一个Context对象,并将需要共享的状态message
和一个更新状态的函数setMessage
作为value传递给子组件。在子组件中,我们通过useContext hook获取这个value对象,并使用其中的属性和方法。
ChildComponent1中可以直接获取到message
的值并渲染在组件中。ChildComponent2中则可以通过调用setMessage
方法更新message
的值。
需要注意的是,在使用Context时需要确保Provider组件包含了所有需要访问的子组件,否则子组件可能无法获取到正确的Context值。
26.跨组件传值方式
React中跨组件传值可以使用以下几种方式:
-
使用React的上下文(Context)API:提供了一种在组件之间共享值的方式,不需要显示地通过props进行逐层传递。参考上一个回答。
-
使用Redux:通过创建一个全局的store来管理应用程序的状态,并通过dispatch action来更新状态和通知组件。Redux提供了一个Provider组件,可以将store注入到组件树中,使所有组件都可以访问到它。详见Redux官网
-
使用事件总线(Event Bus):可以使用第三方库,如React EventBus、EventEmitter3等来实现。通过在事件总线上绑定事件和触发事件的方式来实现跨组件传值。
-
使用React Router:通过URL传递参数来实现跨组件传值。可以使用query string、hash等方式传递参数。在接收参数的组件中通过props.location来获取参数。详见React Router官网
需要根据实际情况选择适合的方法进行跨组件传值。如果只是单纯的父子组件之间的传值,比较简单,可以直接使用props传递。如果组件之间的关系比较复杂,或者需要共享的状态比较多,可以考虑使用Redux或Context来管理应用状态。如果只是需要传递少量数据,可以考虑使用事件总线或React Router。
27.你对“单一数据源”有什么理解?
“单一数据源”指的是在系统中只有一处数据存储的情况,数据来源统一、唯一且规范,不同的业务模块或组件都通过访问这个数据源来进行数据的读取和写入。这种架构能够保证数据的一致性和准确性,同时还能够提高数据的安全性和可维护性,避免了数据的重复存储和不一致的情况。单一数据源还能够降低系统的复杂性和维护成本,方便系统的扩展和升级。
在React应用中,“单一数据源”通常指的是应用的状态(state)被统一存储在一个顶层组件(如App组件)的state中,而其他组件通过props来获取和修改数据。这个顶层组件可以作为应用的“单一数据源”,通过这种方式,所有的状态都被集中在同一个地方进行管理,避免了状态的冗余和不一致。
React提供了一种称为“状态提升”的技术,即将某个组件的状态提升到它的父组件中。这样,子组件的状态就变成了父组件的状态,实现了组件之间状态的共享和管理。通过这种方式,React能够方便地实现“单一数据源”的架构,保证应用状态的一致性和可维护性。
Redux是一个用于JavaScript应用程序的状态管理库。它遵循一种被称为“单一数据源”的架构模式,这意味着应用程序的整个状态都保存在一个单一的JavaScript对象中,通常称为“store”。
在Redux中,应用程序状态被认为是不可变的,不能直接修改。取而代之的是,只能通过“action”来描述一些变化,这些action是一个包含描述变化的数据的普通JavaScript对象。
当action被分发(或者说被“dispatch”)到store时,store会根据该action来更新状态。这种“单向数据流”的模式使得状态变化管理更加可预测和可控,避免了一些常见的状态管理问题,比如难以跟踪状态变化、状态不一致等。
因此,“单一数据源”是Redux的核心理念之一,它使得整个应用程序的状态都保存在一个单一的JavaScript对象中,方便管理和维护。另外,Redux还提供了一些常用的工具和规则(如“纯函数”、“中间件”等)来帮助开发者更好地管理应用程序的状态。
28.事件this怎么修改
在React中,事件处理函数的this默认指向undefined,而非组件实例(或者说是指向null)。因此,如果要在事件处理函数中访问组件的实例属性或者调用组件的方法,需要将this绑定到组件实例上。
有多种方式可以绑定事件处理函数的this,以下是几种常用的方式:
1.在构造函数中使用bind()方法绑定this:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
// 在这里使用组件实例的属性或方法
}
render() {
return (
<button onClick={this.handleClick}>Click me</button>
);
}
}
2.使用箭头函数定义事件处理函数,因为箭头函数自动继承上下文:
class MyComponent extends React.Component {
handleClick = () => {
// 在这里使用组件实例的属性或方法
}
render() {
return (
<button onClick={this.handleClick}>Click me</button>
);
}
}
3.使用Function.prototype.bind()方法:
class MyComponent extends React.Component {
handleClick() {
// 在这里使用组件实例的属性或方法
}
render() {
return (
<button onClick={this.handleClick.bind(this)}>Click me</button>
);
}
}
无论使用哪种方式,都需要将this绑定到组件实例上,以便在事件处理函数中使用组件的属性和方法。
Redux解决了什么问题?是如何工作的?
React框架这样的纯视图的框架对组件通信、状态共享等方面没有太好的解决方案,只能利用状态提升、context之类的技术方案来解决, 但是当组件结构、数据流向都比较复杂的时候,React本身的Api解决这样的问题就有些吃力。
此时就可以使用Flux、Redux这样的架构方式来解决问题。
我们之前的项目就在使用Redux,后来又有了rtk,让redux在react组件中的使用更加方便,在一些小型的项目, 或者一些小模块中, 利用useReducerHook来进行state与dispatch的构建,也能快速优雅的解决问题。
之前的原生Redux中,真实项目开发中,往往需要搭配redux-thunk来进行异步action处理,以及react-redux来进行组件与store的连接, 如果不使用react-redux的话,会比较繁琐。
redux的结构分为store、Views、Actions、Reducer。
Store中存储的状态在视图中可以通过getState来获取,也可以通过subscribe方法进行监听,当视图产生UI操作的时候, 可以调用actions的方法来生成action后,利用dispatch方法进行派发,此时reducer就会被调用,并且接收到当前的状态与action对象, 经过计算出,返回新状态,Store就会进行状态的更新。
利用react-redux后, 组件可以通过connect搭配mapStateToProps及mapDispatchToProps参数来获取store中的状态及actions中的方法。
使用RTK之后,创建reducer更近方便,利用useSelector和useDispatch可以更快更高效取用状态及派发action。
对于一个小的场景,比如之前我做的注册模块有三个步骤,每个步骤都需要用到第一步用户填写的手机号等信息,这样就形成了几个小组件间的状态共享, 如果使用父组件状态提升会导致数据流向不清晰,也犯不上使用redux-store进行存储,于是就是使用useReducer快速的创建了一个小的store,内部集成了state与dispatch,搭配Context,也能很高效的解决问题
redux是如何通知react数据变更,说通知的过程?
redux中store中的状态需要利用getState方法来获取,在组件中更新的场景只有属性和状态变化才能引起组件的re-render,所以在正常开发中,往往需要将store中的状态对应的挂载到组件自身的state上,等到store中状态变化的时候同时更新组件自己的状态,这样就能引起组件的重新渲染最新的数据。
这个时候就需要利用store,.subscribe方法来监听store中状态的变化, 当store中状态变化,组件再去更新自己的状态。
利用react-redux的connect之后,就不要写这样的代码了,因为connect根据mapStateToProps生成的容器组件已经去监听状态变化,UI组件只需要在属性上等待接收即可。
React中key的作用
React组件在更新的时候,react就会生成新的完整的虚拟DOM树与之前的虚拟dom进行比对,然后再对有更新的节点相关的位置进行更新。
对比之前往往需要进行匹配和比对,为了匹配的更精准,react希望在列表循环等位置去手动为Element添加key属性,这样对比的时候就可以通过查找key来进行精准匹配。
我之前做项目的时候也碰到过这样的情况,一般都是用数据的id来作为key, 有一次出现了问题,后端给的数据里头没有id这样的主键·,顺手用了索引做key,在数据列表的顶部添加item的时候对比出错了,最后还是找后端添加唯一id才处理好。
用hooks编程的话如何取模拟生命周期?
函数组件中利用hooks模拟生命周期主要使用的就是useEffect。
如果useEffect的依赖数组为空数组, 此时,模拟的是componentDidMount;
如果useEffect的依赖数组不为空,此时, 模拟的是componnetDidUpdate;
如果useEffect的回调函数中返回一个函数, 此时,这个返回的函数模拟的是componentWillUnmount。