|
| 1 | +--- |
| 2 | +title: 'React Native 0.74' |
| 3 | +authors: |
| 4 | + [hurali, alanjhughes, alfonsocj, ingridwang, cortinico, huntie] |
| 5 | +tags: [release] |
| 6 | +date: 2024-04-15 |
| 7 | +--- |
| 8 | + |
| 9 | +Today we're releasing React Native 0.74!... |
| 10 | + |
| 11 | +## Notable |
| 12 | + |
| 13 | +- Yoga 3.0 |
| 14 | +- Batched updates for `onLayout` (New Architecture only?) |
| 15 | +- Bridgeless in New Architecture? |
| 16 | +- Deprecated APIs in `PushNotificationIOS` |
| 17 | +- Removed [deprecated prop types](https://github.com/facebook/react-native/commit/228cb80af9ded20107f3c7a30ffe00e24471bfeb) |
| 18 | +- Android SDK minimum bump (Android 6.0) |
| 19 | + |
| 20 | +<!--truncate--> |
| 21 | + |
| 22 | +## Yoga 3.0 |
| 23 | + |
| 24 | +- Link to Yoga post, need clarification on how it affects users. |
| 25 | + |
| 26 | +## Batched `onLayout` updates (New Architecture) |
| 27 | + |
| 28 | +State updates in `onLayout` callbacks are now batched. Prior, each state update in the `onLayout` event would result in a new render commit. |
| 29 | + |
| 30 | +```jsx |
| 31 | +function MyComponent(props) { |
| 32 | + const [state1, setState1] = useState(false); |
| 33 | + const [state2, setState2] = useState(false); |
| 34 | + |
| 35 | + return ( |
| 36 | + <View> |
| 37 | + <View |
| 38 | + onLayout={() => { |
| 39 | + setState1(true); |
| 40 | + }}> |
| 41 | + <View |
| 42 | + onLayout={() => { |
| 43 | + // When this event is executed, state1's new value is no longer observable here. |
| 44 | + setState2(true); |
| 45 | + }}> |
| 46 | + </View> |
| 47 | + </View> |
| 48 | + ); |
| 49 | +} |
| 50 | +``` |
| 51 | + |
| 52 | +In 0.74, `setState1` and `setState2` updates are batched together. This change is [expected behavior in React](https://react.dev/learn/queueing-a-series-of-state-updates#react-batches-state-updates) and allows for less re-renders. |
| 53 | + |
| 54 | +:::danger |
| 55 | +This change may break code that has relied on un-batched state updates. You'll need to refactor this code to use [updater functions](https://react.dev/learn/queueing-a-series-of-state-updates#updating-the-same-state-multiple-times-before-the-next-render) or equivalent. |
| 56 | +::: |
| 57 | + |
| 58 | +## Deprecated APIs in `PushNotificationIOS` |
| 59 | + |
| 60 | +:::warning |
| 61 | +The [PushNotificationIOS](https://reactnative.dev/docs/pushnotificationios) library is marked as deprecated. The changes in this release are focused on removing references to deprecated iOS APIs. In a future release, this library will be moved out of react-native core and converged with the community package, [@react-native-community/push-notification-ios](https://github.com/react-native-push-notification/ios). |
| 62 | +::: |
| 63 | + |
| 64 | +[PushNotificationIOS](https://reactnative.dev/docs/pushnotificationios) has been migrated onto Apple’s [User Notification](https://developer.apple.com/documentation/usernotifications?language=objc) framework and exposes new APIs for scheduling and handling notifications. If you are still relying on PushNotificationIOS, you’ll need to migrate over before the next release (0.75) when the deprecated APIs will be removed. |
| 65 | + |
| 66 | +### API Changes |
| 67 | + |
| 68 | +The `didRegisterUserNotificationSettings:` callback on `RCTPushNotificationManager` was a no-op and has been deleted. |
| 69 | + |
| 70 | +The following callbacks on `RCTPushNotificationManager` have been deprecated and will be removed in 0.75: |
| 71 | + |
| 72 | +```objectivec |
| 73 | ++ (void)didReceiveLocalNotification:(UILocalNotification *)notification; |
| 74 | ++ (void)didReceiveRemoteNotification:(NSDictionary *)notification; |
| 75 | +``` |
| 76 | + |
| 77 | +In order to retrieve the notification which launched the app using `getInitialNotification()`, you’ll now need to explicitly set the `initialNotification` on `RCTPushNotificationManager`: |
| 78 | + |
| 79 | +```objectivec |
| 80 | +[RCTPushNotificationManager setInitialNotification:response.notification]; |
| 81 | +``` |
| 82 | +
|
| 83 | +On the JS side, properties on `Notification` have changed. `alertAction` and `repeatInterval` are now deprecated and will be removed in 0.75: |
| 84 | +
|
| 85 | +```js |
| 86 | +type Notification = {| |
| 87 | + ... |
| 88 | + +fireDate?: ?number, |
| 89 | + /** NEW. Seconds from now to display the notification. */ |
| 90 | + +fireIntervalSeconds?: ?number, |
| 91 | + /** |
| 92 | + * CHANGED. Used only for scheduling notifications. Will be null when |
| 93 | + * retrieving notifications using `getScheduledLocalNotifications` or |
| 94 | + * `getDeliveredNotifications`. |
| 95 | + */ |
| 96 | + +soundName?: ?string, |
| 97 | + /** DEPRECATED. This was used for iOS's legacy UILocalNotification. */ |
| 98 | + +alertAction?: ?string, |
| 99 | + /** DEPRECATED. Use `fireDate` or `fireIntervalSeconds` instead. */ |
| 100 | + +repeatInterval?: ?string, |
| 101 | +|}; |
| 102 | +
|
| 103 | +``` |
| 104 | + |
| 105 | +Finally, the `handler` parameter on `PushNotificationIOS.removeEventListener` is unused and has been removed. |
| 106 | + |
| 107 | +### How to Migrate |
| 108 | + |
| 109 | +#### iOS |
| 110 | + |
| 111 | +Your `AppDelegate` will need to implement `UNUserNotificationCenterDelegate`. This should be done on app startup in `application:willFinishLaunchingWithOptions:` or `application:didFinishLaunchingWithOptions:` (see [Apple Docs](https://developer.apple.com/documentation/usernotifications/unusernotificationcenterdelegate?language=objc) for more details). |
| 112 | + |
| 113 | +```objectivec |
| 114 | +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions |
| 115 | +{ |
| 116 | + ... |
| 117 | + UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; |
| 118 | + center.delegate = self; |
| 119 | + |
| 120 | + return YES; |
| 121 | +} |
| 122 | +``` |
| 123 | + |
| 124 | +Implement <code>[userNotificationCenter:willPresentNotification:withCompletionHandler:](https://developer.apple.com/documentation/usernotifications/unusernotificationcenterdelegate/1649518-usernotificationcenter?language=objc)</code>, which is called when a notification arrives and the app is in the <em>foreground</em>. Use the <code>completionHandler</code> to determine if the notification will be shown to the user and notify <code>RCTPushNotificationManager</code> accordingly: |
| 125 | + |
| 126 | +```objectivec |
| 127 | +- (void)userNotificationCenter:(UNUserNotificationCenter *)center |
| 128 | + willPresentNotification:(UNNotification *)notification |
| 129 | + withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler |
| 130 | +{ |
| 131 | + // This will trigger 'notification' and 'localNotification' events on PushNotificationIOS |
| 132 | + [RCTPushNotificationManager didReceiveNotification:notification]; |
| 133 | + // Decide if and how the notification will be shown to the user |
| 134 | + completionHandler(UNNotificationPresentationOptionNone); |
| 135 | +} |
| 136 | +``` |
| 137 | + |
| 138 | +To handle when a notification is tapped, implement <code>[userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:](https://developer.apple.com/documentation/usernotifications/unusernotificationcenterdelegate/1649501-usernotificationcenter?language=objc)</code>. Note that if you set foreground notifications to be shown in <code>userNotificationCenter:willPresentNotification:withCompletionHandler:</code>, you should only notify <code>RCTPushNotificationManager</code> in one of these callbacks. |
| 139 | + |
| 140 | +If the tapped notification resulted in app launch, call `setInitialNotification:`. If the notification was not previously handled by `userNotificationCenter:willPresentNotification:withCompletionHandler:`, call `didReceiveNotification:` as well: |
| 141 | + |
| 142 | +```objectivec |
| 143 | +- (void) userNotificationCenter:(UNUserNotificationCenter *)center |
| 144 | + didReceiveNotificationResponse:(UNNotificationResponse *)response |
| 145 | + withCompletionHandler:(void (^)(void))completionHandler |
| 146 | +{ |
| 147 | + // This condition passes if the notification was tapped to launch the app |
| 148 | + if ([response.actionIdentifier isEqualToString:UNNotificationDefaultActionIdentifier]) { |
| 149 | + // Allow the notification to be retrieved on the JS side using getInitialNotification() |
| 150 | + [RCTPushNotificationManager setInitialNotification:response.notification]; |
| 151 | + } |
| 152 | + // This will trigger 'notification' and 'localNotification' events on PushNotificationIOS |
| 153 | + [RCTPushNotificationManager didReceiveNotification:response.notification]; |
| 154 | + completionHandler(); |
| 155 | +} |
| 156 | +``` |
| 157 | + |
| 158 | +Finally, delete the following methods and adapt the logic into the callbacks above which will be called instead: |
| 159 | + |
| 160 | +1. <code>[application:didReceiveLocalNotification:](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622930-application?language=objc)</code> [deprecated] |
| 161 | +2. <code>[application:didReceiveRemoteNotification:](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623117-application?language=objc)</code> [deprecated] |
| 162 | +3. <code>[application:didReceiveRemoteNotification:fetchCompletionHandler:](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623013-application?language=objc)</code> [not deprecated, but is superseded by the <code>UNUserNotificationCenterDelegate</code> methods] |
| 163 | + |
| 164 | +Delete any usages of <code>[application:didRegisterUserNotificationSettings:](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623022-application?language=objc)</code> and <code>RCTPushNotificationManager</code>’s corresponding <code>didRegisterUserNotificationSettings:</code> as well. |
| 165 | + |
| 166 | +**Example:** See the RNTester <code>[AppDelegate.mm](https://github.com/facebook/react-native/blob/main/packages/rn-tester/RNTester/AppDelegate.mm)</code>. |
| 167 | + |
| 168 | +#### JS |
| 169 | + |
| 170 | +1. Remove any references to `alertAction`. |
| 171 | +2. Remove the `handler` argument on any calls to `removeEventListener`. |
| 172 | +3. Replace any usages of `repeatInterval` by firing multiple notifications using `fireDate` or `fireIntervalSeconds` instead. |
| 173 | +4. Note that `soundName` will be null when it is accessed on a `Notification` returned from `getScheduledLocalNotifications()` and `getDeliveredNotifications()`. |
| 174 | + |
| 175 | +## Acknowledgements |
| 176 | + |
| 177 | +- |
| 178 | + |
| 179 | +## Upgrade to 0.74 |
| 180 | + |
| 181 | +Please use the [React Native Upgrade Helper](https://react-native-community.github.io/upgrade-helper/) to view code changes between React Native versions for existing projects, in addition to the [Upgrading docs](/docs/upgrading). You can also create a new project with `npx react-native@latest init MyProject`. |
| 182 | + |
| 183 | +If you use Expo, React Native 0.73 will be supported in the Expo SDK 50 release. |
| 184 | + |
| 185 | +:::info |
| 186 | +0.73 is now the latest stable version of React Native and **0.70.x now moves to unsupported**. For more information see [React Native’s support policy](https://github.com/reactwg/react-native-releases#releases-support-policy). |
| 187 | +::: |
0 commit comments