简体   繁体   English

React 中副作用的定义和函数式编程中的一样吗?

[英]Is the definition of side effects in React the same as in functional programming?

I'm learning both React and functional programming at the same time.我正在同时学习 React 和函数式编程。 When I was learning about the concept of side effects, I feel that the definition of side effects are a slightly different in React and functional programming.当我在学习副作用的概念时,我觉得副作用的定义在 React 和函数式编程中略有不同。

In functional programming, if a function has an internal state, making changes to that internal state is a side effect.在函数式编程中,如果 function 具有内部 state,则对该内部 state 进行更改是副作用。

However, when an internal state is created by useState within a React function component, modifying that internal state doesn't seem to be a side effect.但是,当useState在 React function 组件中创建内部 state 时,修改该内部 state 似乎不是副作用。

Did I miss something?我错过了什么? Or is the concept of side effects really different in React and functional programming?或者 React 和函数式编程中副作用的概念真的不同吗? Thank you.谢谢你。

The mathematical definition of a function is a mapping from input to output. And nothing else. function 的数学定义是从输入到 output 的映射。仅此而已。 So (x) => x + 1 is a function. The output depends only on the input, not on the contents of some file system, or on a.network connection, or on user input, or on a random number generator.所以(x) => x + 1是 function。output取决于输入,而不取决于某些文件系统的内容、网络连接、用户输入或随机数生成器。 "Side effects" are when a function deviates from this definition. “副作用”是指 function 偏离此定义。

So a function () => Math.random() is not an actual "mathematical" function, since you can pass it the same inputs (namely, none of them) and get different outputs.所以 function () => Math.random()不是实际的“数学”function,因为您可以向它传递相同的输入(即没有输入)并获得不同的输出。 Functional languages get around this by saying the random state is really just another parameter.函数式语言通过说随机 state 实际上只是另一个参数来解决这个问题。 So in Haskell, we would do something like this: (gen) => gen.random() , where gen is the random number generation state. Now this is a pure function with no side effects.所以在 Haskell 中,我们会做这样的事情: (gen) => gen.random() ,其中gen是随机数生成 state。现在是一个没有副作用的纯 function。 If we give it the same input (namely, the same generator state), it'll give us the same output consistently.如果我们给它相同的输入(即相同的生成器状态),它会始终如一地给我们相同的 output。 This is the philosophical viewpoint functional programming is coming from.这就是函数式编程的哲学观点。

React's notion of "side effects" is meant to prevent things outside of React's control. React 的“副作用”概念旨在防止 React 无法控制的事情发生。 React wants you to move all of your (mathematical) side effects into the internal state, which React controls. React 希望您将所有(数学)副作用移动到 React 控制的内部 state 中。 That doesn't make your function any more of a mathematical function;那不会使您的 function 不再是数学上的 function; the definition of "function" remains the same. “功能”的定义保持不变。 It just means that React can see those side effects.这只是意味着 React 可以看到那些副作用。

Yes, both usages of the term "side effect" mean the same thing .是的,“副作用”一词的两种用法意思相同

However, when an internal state is created by useState within a React function component, modifying that internal state doesn't seem to be a side effect.但是,当useState在 React function 组件中创建内部 state 时,修改该内部 state 似乎不是副作用。

I would argue that calling useState does not create internal state, and certainly that doesn't modify it.我认为调用useState不会创建内部 state,当然也不会修改它。

It's a pity that function components are not truly pure functions , even though they're sometimes marketed as such.遗憾的是 function 组件并不是真正的纯函数,尽管它们有时被如此推销。 (At least they're officially called "function components" - constrasted to "class components" -, not "function al components"). (至少它们被正式称为“功能组件”——与“类组件”相对照——而不是“功能组件”)。 They do rely a lot on global state, namely React setting up the context for the evaluation of the component function for rendering it.他们确实非常依赖全局 state,即React为组件 function 的评估设置上下文以渲染它。 This is why you cannot just call them like Counter() , you have to use ReactDOM.render(<Counter>, …) .这就是为什么你不能像Counter()那样调用它们,你必须使用ReactDOM.render(<Counter>, …) A component such as一个组件如

function Counter(props) {
   const [count, setCount] = useState(0);
   function increment() {
     setCount(c => c+1);
   }
   return <div>
     <p>Count: {count}</p>
     <button onClick={increment}>+1</button>
   </div>;
}

might better be written as最好写成

const key = Symbol()
function Counter(__opaqueReactContext, props) {
   const [count, setCount] = __opaqueReactContext.useState(key, 0);
   function increment() {
     setCount(c => c+1);
   }
   return <div>
     <p>Count: {count}</p>
     <button onClick={increment}>+1</button>
   </div>;
}

(The key is used to identify the first useState call and separate it from other useState calls in the component, in reality React just counts the invocations and complains if you don't follow the "rules of hooks" ) key用于识别第一个useState调用并将其与组件中的其他useState调用分开,实际上 React 只是计算调用次数并在您不遵循“钩子规则”时抱怨)

to be pure, but React doesn't want to hand out an __opaqueReactContext that a) is not immutable for efficiency reasons and b) might be stored/references by badly written components and c) is not ergonomic to write.是纯粹的,但 React 不想分发一个__opaqueReactContext ,a)出于效率原因不是不可变的,b)可能被写得不好的组件存储/引用,c)不符合人体工程学。

Still, this is how you should think of a component as a pure function: the return value depends only on the argument values.不过,这仍然是您应该将组件视为纯 function 的方式:返回值仅取决于参数值。 There are no side effects during the evaluation of the function, React can (and in strict mode actually does for verification) run it multiple times and will get the same result (which is just an immutable description of elements to be rendered, nothing stateful either).在 function 的评估期间没有副作用,React 可以(并且在严格模式下实际上用于验证)多次运行它并会得到相同的结果(这只是对要呈现的元素的不可变描述,也没有任何状态).

Now, where are the effects happening then, how does our application keep state?现在,影响发生在哪里,我们的应用程序如何保留 state? It somehow has to modify the DOM to be useful , and that's what ReactDOM.render does.必须以某种方式修改 DOM 才能发挥作用,这就是ReactDOM.render所做的。 It will create and manage the state, in a complex tree hierarchy of component states.它将在组件状态的复杂树层次结构中创建和管理 state。 All the effects are under React's control here.这里所有的效果都在 React 的控制之下。 This includes procedures (imperative code) that the user wrote, such as the increment click handler in the above example, or a useEffect callback.这包括用户编写的过程(命令式代码),例如上例中的increment点击处理程序或useEffect回调。 React decides when to execute them, and if they run an effect (such as calling setCount ), it will adjust its component model and run the "pure" component functions to rerender - with a different __opaqueReactContext . React 决定何时执行它们,如果它们运行效果(例如调用setCount ),它将调整其组件 model 并运行“纯”组件函数以重新渲染 - 使用不同的__opaqueReactContext

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM