or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

act-utilities.mdasync-testing.mdcleanup-management.mderror-handling.mdhook-rendering.mdindex.mdserver-side-rendering.md

act-utilities.mddocs/

0

# Act Utilities

1

2

State update wrapping utilities that ensure proper timing and batching of React updates during testing. The `act` utility is essential for testing hooks that perform state updates or side effects.

3

4

## Capabilities

5

6

### act Function

7

8

Wraps code that causes React state updates, ensuring that all updates are flushed and effects are executed before the act call completes. This is crucial for predictable testing of hooks.

9

10

```typescript { .api }

11

/**

12

* Wraps synchronous code that causes state updates

13

* @param callback - Synchronous function that triggers state updates

14

*/

15

function act(callback: () => void | undefined): void;

16

17

/**

18

* Wraps asynchronous code that causes state updates

19

* @param callback - Asynchronous function that triggers state updates

20

* @returns Promise that resolves when all updates are complete

21

*/

22

function act(callback: () => Promise<void | undefined>): Promise<undefined>;

23

```

24

25

**Usage Examples:**

26

27

```typescript

28

import { renderHook, act } from "@testing-library/react-hooks";

29

import { useState } from "react";

30

31

function useCounter(initialCount = 0) {

32

const [count, setCount] = useState(initialCount);

33

const increment = () => setCount(prev => prev + 1);

34

const decrement = () => setCount(prev => prev - 1);

35

return { count, increment, decrement };

36

}

37

38

test("synchronous state updates with act", () => {

39

const { result } = renderHook(() => useCounter(0));

40

41

expect(result.current.count).toBe(0);

42

43

// Wrap synchronous state updates in act

44

act(() => {

45

result.current.increment();

46

});

47

48

expect(result.current.count).toBe(1);

49

50

act(() => {

51

result.current.increment();

52

result.current.increment();

53

});

54

55

expect(result.current.count).toBe(3);

56

});

57

58

// Asynchronous example

59

function useAsyncCounter() {

60

const [count, setCount] = useState(0);

61

62

const incrementAsync = async () => {

63

await new Promise(resolve => setTimeout(resolve, 100));

64

setCount(prev => prev + 1);

65

};

66

67

return { count, incrementAsync };

68

}

69

70

test("asynchronous state updates with act", async () => {

71

const { result } = renderHook(() => useAsyncCounter());

72

73

expect(result.current.count).toBe(0);

74

75

// Wrap asynchronous state updates in act

76

await act(async () => {

77

await result.current.incrementAsync();

78

});

79

80

expect(result.current.count).toBe(1);

81

});

82

```

83

84

### When to Use act

85

86

Use `act` whenever you're triggering state updates or side effects in your hooks during tests:

87

88

**State Updates:**

89

```typescript

90

// ✅ Correct - wrap state updates

91

act(() => {

92

result.current.setValue("new value");

93

});

94

95

// ❌ Incorrect - unwrapped state update

96

result.current.setValue("new value");

97

```

98

99

**Effect Triggers:**

100

```typescript

101

function useDebounce(value: string, delay: number) {

102

const [debouncedValue, setDebouncedValue] = useState(value);

103

104

useEffect(() => {

105

const timer = setTimeout(() => setDebouncedValue(value), delay);

106

return () => clearTimeout(timer);

107

}, [value, delay]);

108

109

return debouncedValue;

110

}

111

112

test("debounce hook", async () => {

113

jest.useFakeTimers();

114

const { result, rerender } = renderHook(

115

({ value, delay }) => useDebounce(value, delay),

116

{ initialProps: { value: "initial", delay: 500 } }

117

);

118

119

expect(result.current).toBe("initial");

120

121

// Update props that trigger useEffect

122

act(() => {

123

rerender({ value: "updated", delay: 500 });

124

});

125

126

// Fast-forward timers

127

act(() => {

128

jest.advanceTimersByTime(500);

129

});

130

131

expect(result.current).toBe("updated");

132

});

133

```

134

135

**Event Handlers:**

136

```typescript

137

function useClickCounter() {

138

const [count, setCount] = useState(0);

139

const [ref, setRef] = useState<HTMLElement | null>(null);

140

141

useEffect(() => {

142

if (!ref) return;

143

144

const handleClick = () => setCount(prev => prev + 1);

145

ref.addEventListener("click", handleClick);

146

147

return () => ref.removeEventListener("click", handleClick);

148

}, [ref]);

149

150

return { count, ref: setRef };

151

}

152

153

test("click counter hook", () => {

154

const { result } = renderHook(() => useClickCounter());

155

156

const element = document.createElement("button");

157

158

act(() => {

159

result.current.ref(element);

160

});

161

162

expect(result.current.count).toBe(0);

163

164

act(() => {

165

element.click();

166

});

167

168

expect(result.current.count).toBe(1);

169

});

170

```

171

172

### act with Multiple Updates

173

174

`act` can wrap multiple state updates that should be batched together:

175

176

```typescript

177

function useMultiState() {

178

const [name, setName] = useState("");

179

const [age, setAge] = useState(0);

180

const [email, setEmail] = useState("");

181

182

const updateAll = (newName: string, newAge: number, newEmail: string) => {

183

setName(newName);

184

setAge(newAge);

185

setEmail(newEmail);

186

};

187

188

return { name, age, email, updateAll };

189

}

190

191

test("multiple state updates", () => {

192

const { result } = renderHook(() => useMultiState());

193

194

act(() => {

195

result.current.updateAll("Alice", 25, "alice@example.com");

196

});

197

198

expect(result.current.name).toBe("Alice");

199

expect(result.current.age).toBe(25);

200

expect(result.current.email).toBe("alice@example.com");

201

});

202

```

203

204

### Error Handling in act

205

206

If an error occurs within `act`, it will be propagated:

207

208

```typescript

209

function useErrorHook() {

210

const [shouldError, setShouldError] = useState(false);

211

212

const triggerError = () => setShouldError(true);

213

214

if (shouldError) {

215

throw new Error("Hook error occurred");

216

}

217

218

return { triggerError };

219

}

220

221

test("error handling in act", () => {

222

const { result } = renderHook(() => useErrorHook());

223

224

expect(() => {

225

act(() => {

226

result.current.triggerError();

227

});

228

}).toThrow("Hook error occurred");

229

});

230

```