Javascript-纯函数与不纯函数


12

我已经经历了两个类似的定义:

纯函数是不尝试更改其输入的函数,并且对于相同的输入始终返回相同的结果。

function sum(a, b) {
  return a + b;
}

不纯函数是一种更改其自身输入的函数。

function withdraw(account, amount) {
  account.total -= amount;
}

定义和代码片段摘自ReactJs 官方文档

现在,有人可以告诉我,如何在React / Redux中犯一些错误,以在需要纯函数的地方使用不纯函数?


4
不纯功能会产生副作用。诸如此类之类的东西,window.getElementById使具有相同参数的相同功能运行会产生不同的结果,具体取决于副作用。这是redux失败的地方。
多米尼克

Answers:


10

React和Redux两者都需要纯功能和不变性,以可预测的方式运行。

如果您不遵循这两件事,则您的应用程序将存在错误,最常见的错误是React/Redux无法跟踪更改,并且无法在state/prop更改时重新呈现。

关于React,请考虑以下示例:

let state = {
    add: 0,
}

function render() {
    //...
}
//pure function
function effects(state,action) {
//following immutability while updating state, not directly mutating the state.
    if(action == 'addTen') {
        return {...state, add: state.add + 10} 
    }
    return state;
}

function shouldUpdate(s) {
    if(s === state){
        return false
    }
    return true
}

state = effects(state, 'addTen')if(shouldUpdate(state)) {
    render();
}

状态由仅添加了属性的状态对象保存。此应用程序呈现应用程序属性。它不应该总是在发生任何事情时呈现状态,而应该检查状态对象中是否发生了更改。

像这样,我们有一个effect函数,pure function我们用它来影响状态。您会看到,当要更改状态时它将返回新状态,而在不需要修改时将返回相同状态。

我们还有一个shouldUpdate函数,该函数使用===运算符检查旧状态和新状态是否相同。

要在React方面犯错误,您实际上可以执行以下操作:

function effects(state,action) {

  doRandom(); // effects should only be called for updating state.
             // Doing any other stuff here would make effects impure.

    if(action == 'addTen') {
        return {...state, add: state.add + 10}
    }
    return state;
}

您也可以通过直接设置状态而不使用effects功能来犯错。

function doMistake(newValue) {
    this.state = newValue
}

不应执行上述操作,而effects应仅使用函数来更新状态。

就React而言,我们effects称为setState

对于Redux:

  1. Redux的combineReducers实用程序检查参考更改。
  2. React-Redux的connect方法生成用于检查根状态和mapState函数返回值的引用更改的组件,以查看包装后的组件是否确实需要重新渲染。
  3. 时间旅行调试要求减速器pure functions无副作用,以便您可以在不同状态之间正确跳转。

通过使用不纯函数作为归约器,可以轻松违反以上三个条件。

以下内容直接来自redux文档:

之所以称为简化器,是因为它是传递给的函数的类型Array.prototype.reduce(reducer, ?initialValue)
减速器保持纯净非常重要。在减速器中永远不应该做的事情:

Mutate its arguments;
Perform side effects like API calls and routing transitions;
Call non-pure functions, e.g. Date.now() or Math.random().

给定相同的参数,它应该计算下一个状态并返回它。没什么好奇怪的 没有副作用。没有API调用。没有突变。只是计算而已。


7

简单地说,状态无法改变。每次更改时都应返回状态的新实例,因此

此代码不正确:

const initialStates = {    
  items: ['item1']
}

export const ItemMaster = (state = initialStates, action) => {    
  switch (action.type) {
    case TYPES.ADD_ITEM:            
    {
        state.items.push(action.item)
        return state
    }
    default:
      return state
  }
}

该代码在下面作为纯函数编写时,将返回该数组的新实例,但不会修改实际的数组本身。这就是为什么您应该使用像immer这样的库来处理不变性的原因

const initialStates = { 
  items: ['item1']
}

export const ItemMaster = (state = initialStates, action) => {    
  switch (action.type) {
    case TYPES.ADD_ITEM:            
    {

        state = {...state,items:state.items.concat(action.item)}
        return state
    }
    default:
      return state
  }
}

5

您可以通过添加API调用或编写导致副作用的代码来使纯函数不纯。

纯粹的功能应该始终是针对性的,并且不言自明,并且不应该要求您引用其他3个或4个功能来了解正在发生的事情。

// Pure Function
function USDtoEUR(USD, todayRate) {
  return USD * todayRate;
}

// Impure Function 
function USDtoEUR(USD) {
  const todayRate = getTodayRate();
  return USD * todayRate;
}

如果是React / Redux

const mapState = async state => {
  const { data } = await whatDoINeed()

  let mappedState = {}

  if (data.needDolphin) {
    mappedState.dolphin = state.dolphin
  }

  if (data.needShark) {
    mappedState.shark= state.shark
  }

  return mappedState;
}

// Or for Redux Reducer
// Bad
{
  setData: (state, payload) => {
   const set = whatToSet()
   return {
     ...state,
     set.dolphin ? ...{ dolphin: payload.dolphin } : ...{},
     set.shark ? ...{ shark : payload.shark } : ...{},
   }
  }
}

// Good
{
  setData: (state, payload) => {
   return {
     ...state,
     // Just send only the things need
     // to be sent
     ...payload
   }
  }
}

不应这样做。connect函数或reducer函数所需的所有内容都必须通过参数提供或写在其函数中。它永远不应该从外面得到。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.