useEffect内部的函数调用在玩笑测试中未触发

时间:2019-07-29 03:39:27

标签: reactjs enzyme jest

我有一个组件,单击按钮即可通过props.OnValChange将更新后的值发送给父级。这是在useEffect挂钩中实现的。 如果我在控制台上记录useEffect,则可以看到它正在被调用。但是在我进行的测试中,expect(prop.OnValChange).toHaveBeenCalledTimes(1);表示被调用0次。

组件:

const MyComp = ({OnValChange}) => {
    const [ val, setVal ] = useState(0);

    useEffect(() => {
        console.log("before");
        OnValChange(val);
        console.log("after");
    }, [val]);

    return (
        <button onClick={() => setVal(val + 1)}>Count</button>
    )
}

测试:

it("Sends val to parent when button is clicked", () => {
    const prop = {
        OnValChange: jest.fn();
    }

    const control = mount(<MyComp {...prop} />);

    expect(prop.OnValChange).toHaveBeenCalledTimes(0);
    control.find(button).simulate("click");
    expect(prop.OnValChange).toHaveBeenCalledTimes(1);
}) 

2 个答案:

答案 0 :(得分:0)

要回答您的问题,功能组件中没有“ this”。

但是将道具传递给父母也不是“反应的方式”。

对于此用例,我建议使用Context和Reducers设置全局状态/存储。然后,您可以将任何组件中的任何数据发送到商店中,并在应用程序中的任何位置访问该商店。

以下示例用于打开和关闭抽屉菜单,但是您可以添加任何大小写和任何数据
示例:

import React, { createContext, useReducer } from "react"

const reducer = (state, action) => {
  switch (action.type) {
    case "OPEN_MENU":
    // spread current state and then override the one part of the state you want to update
      return { ...state, menuState: true } 
    case "CLOSE_MENU":
      return { ...state, menuState: false }
    case "TOGGLE_MENU":
      return { ...state, menuState: !state.menuState }
    default:
      return
  }
}

const initialState = { menuState: false }
const AppContext = createContext(initialState)

const AppProvider = props => {
  const [state, dispatch] = useReducer(reducer, initialState)

  return (
    <AppContext.Provider value={{ state, dispatch }}>
      {props.children}
    </AppContext.Provider>
  )
}

export { AppContext, AppProvider }

然后,您只需要将应用程序包装在AppProvider中即可。 (或您命名提供商的任何名称。“ ContextProvider”,“ StateProvider”等。)

设置好商店,Reducer和提供者后,您可以从任何此类组件更新上下文。

import React from 'react' 

import { AppContext } from "../../context/Context"

const MyComponent = () => {
    const { state, dispatch } = useContext(AppContext)
    console.log('state: ', state); // access your state from anywhere
    return (
        <div>
            {/* Update your state from anywhere */}
            <button onClick={() => dispatch({ type: "TOGGLE_MENU" })}>Click me to update your context!</button>
        </div>
    )

}

该示例非常适合简单的事情,但是如果您想在调度中传递值,则可以像这样设置开关

const reducer = (state, action) => {
    const { payload } = action
    switch (action.type) {
        case "SET_SOME_VALUE":
            return { ...state, someValue: payload }
        default:
            return
    }
}

,然后在调用调度时,可以像这样设置有效负载值

dispatch({ type: "SET_SOME_VALUE", payload: myVariable })

有关上下文的更多信息,请参见:https://reactjs.org/docs/context.html

答案 1 :(得分:0)

useEffect在组件首次安装时将始终被调用一次,而在您触发按钮单击时将被第二次调用,因此正确的测试应像这样

Edit determined-heyrovsky-22zpv

it("Sends val to parent when button is clicked", () => {
    const prop = {
        OnValChange: jest.fn();
    }

    const control = mount(<MyComp {...prop} />);

    expect(prop.OnValChange).toHaveBeenCalledTimes(1);
    control.find(button).simulate("click");
    expect(prop.OnValChange).toHaveBeenCalledTimes(2);
})

如果您始终是0次,我怀疑这是enzyme-adapter-react-16版本的问题。当我将版本切换到1.13.0时,将会遇到与您相同的问题,您可以尝试将enzyme -adapter-react-16更新为最新版本。

相关问题