javascript - 了解 React 如何/为什么/何时更新 DOM 以及如何使用它
问题描述
我正在制作一个应用程序,其中有 3 个按钮,其中 2 个对我的问题很重要。一个按钮应该向用户正在查看的网站添加元素,另一个应该删除元素。
我是 React 的新手,感觉我掌握了基础知识,但显然需要更好地理解它。
我的第一个直觉是创建一个数组,我可以将元素推入其中,然后将该数组作为它的内容放在我的一个 JSX.Elements 中。
看起来像这样:
我的 JSX.Element:
answerView = (
<div>
<button onClick={this.removeIceCream}>Poista jäätelö!</button>
<button onClick={this.addIceCream}>Lisää jäätelö!</button>
<button onClick={this.keyInput}>OK</button>
<div>{this.iceCreamCount}</div>
</div>
);
仅向元素添加内容的功能。
addIceCream() {
this.iceCreamCount.push(<li>"Jäätelö"</li>);
}
然而,这最终会在数组 iceCreamCount 中更新,但不会显示在 DOM 中。React 不会对元素的变化做出“反应”,而只会对状态或属性的变化做出反应吗?
这是否意味着为了根据用户输入操作 DOM 元素,包含元素的数组也需要是状态变量?
如果上述确实是不可能的,那么我想帮助我如何在 React 中正确地做到这一点,下面是我这样做的尝试。
我检查了一些教程和示例,并想出了这样的东西:
addIceCream = () => {
this.setState(state => {
const iceCreams = state.iceCreams.concat(state.iceCream);
return {
iceCreams,
iceCream: 'Jäätelö',
};
});
};
我的状态声明如下所示:
this.state = { doorView: true, bunnyView: false, answerView: false, iceCream: "Jäätelö", iceCreams: [] };
在写这个问题的过程中,我实际上得到了这个看似有效的方法,但我也想了解它是如何以及为什么会起作用的,因为我对自己的知识并不十分自信。
但是我不觉得我已经掌握了这里到底发生了什么。我对箭头函数不是很有信心(如果有人有关于它们的简单教程,请做链接),虽然我认为我已经收集到了这一点,因为数据在 React 中应该是不可变的,这就是我需要用来
concat()
创建的原因一个新数组,它是旧数组的副本 + 我添加的元素。但是为什么我需要return()
状态变量以及为什么会有这么多箭头函数被抛出(见下文我的浓缩“我想知道的列表”)?
我想知道的
- 我的问题的第一部分在 React 中是一种可行的方法,还是完全是错误的方法?如果可以做到,我将不胜感激,如果有人能告诉我我的想法是如何以及我的错误是什么。
- 我的问题的第二部分为什么以及如何工作?如果有人有时间和耐心从:开始一行一行地阅读它,
addIceCream = () => {....
并告诉我发生了什么,这将对我的理解和学习有很大帮助。
解决方案
我将在下面回答问题。
回答第一个问题
我的问题的第一部分在 React 中是一种可行的方法,还是完全是错误的方法?如果可以做到,我将不胜感激,如果有人能告诉我我的想法是如何以及我的错误是什么。
不幸的是,第一种方法是“完全错误的方法”(在 React 世界中)。
addIceCream() {
this.iceCreamCount.push(<li>"Jäätelö"</li>);
}
然而,这最终会在数组 iceCreamCount 中更新,但不会显示在 DOM 中。React 不会对元素的变化做出“反应”,而只会对状态或属性的变化做出反应吗?
就是这样。React 对React 跟踪的 state & props 变化做出“反应” 。
这是否意味着为了根据用户输入操作 DOM 元素,包含元素的数组也需要是状态变量?
这取决于。一些控件是“受控的”,另一个是“不受控的”(主要适用于表单字段。)
大多数时候,您会使用(React)“受控”选项来保持与 React 关联的所有状态以进行跟踪。(在this.state ={...}
使用钩子的类组件或函数组件中,const [state, setState] = React.useState(...)
)
React 的Reconciliation(一种奇特的说法,知道发生了什么变化以及要呈现什么)算法通过检查更改的状态/道具“引用”(而不是值)来工作。
当你这样做时this.iceCreamCount.push(<li>"Jäätelö"</li>)
,你基本上是在改变一个数组的值,“this.iceCreamCount”,而不是它的引用。
而且在重新分配this.iceCreamCount
给一个新对象之后,你还需要this.setState({iceCreamCount: newIceCreamCount})
通知 React 有些东西已经改变并且需要重新渲染。
现在我们知道了为什么第一种方法不起作用,让我们继续您的解决方法。
回答第二个问题
我的问题的第二部分为什么以及如何工作?如果有人有时间和耐心从以下开始逐行阅读: addIceCream = () => {.... 并告诉我发生了什么,这将对我的理解和学习有很大帮助。
state declaration
this.state = {
doorView: true,
bunnyView: false,
answerView: false,
iceCream: "Jäätelö",
iceCreams: []
};
addIceCream = () => {
this.setState(state => {
const iceCreams = state.iceCreams.concat(state.iceCream);
return { iceCreams, iceCream: "Jäätelö" }
};
});
};
当您查看 时addIceCream
,您确实在创建一个新的参考,iceCreams
。
正如上面问题 #1 中所解释的,这就是 React 的协调算法如何知道状态已更改的方式。
要点是 React 经过优化以检查引用更改,因为深度属性检查(假设this.state
具有深度嵌套的对象,然后比较每个值将花费太长时间)
最后你会返回一个新的引用return { iceCreams, iceCream: "Jäätelö" }
,它会通知 React 有些事情确实发生了变化。
跟进评论。
这里的箭头函数是如何工作的,为什么“addIceCream”函数需要它们?
您已addIceCream
使用箭头函数语法声明。
正如下面一节中提到的,没有分隔 this,箭头函数不会创建自己的this
变量。
但是当你这样做时,由于范围规则的魔力,this
它被设置为父上下文。this
addIceCream = () => {
this.setState(state => {
const iceCreams = state.iceCreams.concat(state.iceCream);
return { iceCreams, iceCream: "Jäätelö" }
};
});
};
这就是为什么你可以this.setState
在上面调用。JavaScript 引擎将尝试this
在作用域链中找到最近的父级。
如果您已声明addIceCream
为普通方法,
class App extends React.Component {
addIceCream() { ...}
}
thenthis
指向addIceCream
因此,将无法访问App
's this.setState
,它React.Component
在您扩展它时可用。
因此,您会看到一种解决方法,通过将自己的方法传递给它来绑定this
类方法,这会使用当前类的this
.
class App extends React.Component {
constructor(props) {
super(props)
// ... this create a new method with `App`'s `this`.
this.addIceCream = this.addIceCream.bind(this)
}
addIceCream() {
// now that `this` is bound to `App`'s `this`, you can call this
this.setState({...})
}
}
这在您使用类组件 (CC) 时适用。当您使用功能组件 (FC) 时,这无关紧要。
您可以使用 React 的新 Hook(useState或useReducer)来保存状态。
(用于useReducer
复杂的状态管理)
cosnt initialState = {
doorView: true,
bunnyView: false,
answerView: false,
iceCream: "Jäätelö",
iceCreams: []
};
function reducer(state, action) {
switch (action.type) {
case 'ADD_ICECREAM':
const { iceCream } = action.payload
const iceCreams= [...state.iceCreams].concat(iceCream)
return { ...state, iceCream, iceCreams }
default: return state;
}
}
function App() {
const [icecreams, setIceCreams] = React.reducer(reducer, initialState);
// You can use either one now.
const addIceCream = iceCream =>
dispatch({type: 'ADD_ICECREAM', payload: { iceCream }});
// OR
function addIceCream(iceCream) {
dispatch({type: 'ADD_ICECREAM', payload: { iceCream }});
}
}
reducer
函数返回一个新的对象引用,因此会导致 React 重新渲染。
您可能已经注意到箭头语法版本使用const
like const addIceCream = iceCream => ...
。
现在它变成了一个指向函数的指针,因此它应该在使用之前声明,与function
版本不同,因为JavaScript 提升是如何工作的。
推荐阅读
- c - 是什么导致读取访问冲突/溢出?
- r - ggplot2 geom_sf - 显示多边形颜色但不显示名称的地图图例
- python - Flask-SQLalchemy、Oracle 和查询的前置所有者
- java - 如何使用模数和指数/公钥在 Java 中快速破解具有大量数字的 RSA 加密
- json - 在 Postgres 中存储大量 JSON 数据是不可行的,那么有哪些替代方案呢?
- tensorflow - 将 cpu 核心传递给 multiworkermirrored strategy()
- javascript - 没有任何方法从字符串的左开始删除空格
- discord.js - 创建频道和设置频道的麻烦
- xml - MVVM 跨本地:MvxBind 为空白
- javascript - 角度下拉菜单不显示默认值