├── example ├── .watchmanconfig ├── .ruby-version ├── app.json ├── .bundle │ └── config ├── common │ ├── types │ │ └── state-type.ts │ ├── app-actions.ts │ ├── reducer.ts │ ├── store.ts │ └── hooks │ │ └── useCount.ts ├── .eslintrc.js ├── android │ ├── app │ │ ├── src │ │ │ ├── main │ │ │ │ ├── res │ │ │ │ │ ├── values │ │ │ │ │ │ ├── strings.xml │ │ │ │ │ │ └── styles.xml │ │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ └── drawable │ │ │ │ │ │ └── rn_edit_text_material.xml │ │ │ │ ├── java │ │ │ │ │ └── com │ │ │ │ │ │ └── example │ │ │ │ │ │ ├── MainActivity.java │ │ │ │ │ │ └── MainApplication.java │ │ │ │ └── AndroidManifest.xml │ │ │ └── debug │ │ │ │ ├── AndroidManifest.xml │ │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── ReactNativeFlipper.java │ │ ├── debug.keystore │ │ ├── proguard-rules.pro │ │ ├── build_defs.bzl │ │ ├── BUCK │ │ └── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── settings.gradle │ ├── gradle.properties │ ├── build.gradle │ ├── gradlew.bat │ └── gradlew ├── ios │ ├── example │ │ ├── Images.xcassets │ │ │ ├── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ ├── AppDelegate.h │ │ ├── main.m │ │ ├── Info.plist │ │ ├── AppDelegate.m │ │ └── LaunchScreen.storyboard │ ├── example.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── exampleTests │ │ ├── Info.plist │ │ └── exampleTests.m │ ├── Podfile │ ├── example.xcodeproj │ │ ├── xcshareddata │ │ │ └── xcschemes │ │ │ │ └── example.xcscheme │ │ └── project.pbxproj │ └── Podfile.lock ├── .buckconfig ├── .prettierrc.js ├── Gemfile ├── app │ ├── styles │ │ ├── TextStyles.ts │ │ └── ScreenStyles.ts │ └── components │ │ ├── Text.tsx │ │ ├── screens │ │ ├── Screen2.tsx │ │ └── HomeScreen.tsx │ │ ├── navigators │ │ └── AppNavigator.tsx │ │ ├── ExpensiveComponent.tsx │ │ └── CustomNavbar.tsx ├── index.js ├── __tests__ │ └── App-test.js ├── babel.config.js ├── focus.js ├── tsconfig.json ├── .gitignore ├── package.json ├── metro.config.js ├── .flowconfig └── Gemfile.lock ├── .npmignore ├── img.png ├── .gitignore ├── index.d.ts ├── lib └── index.js ├── package.json └── README.md /example/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /example/.ruby-version: -------------------------------------------------------------------------------- 1 | 2.7.4 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | example/* 2 | .idea/* 3 | .node_modules/* 4 | -------------------------------------------------------------------------------- /example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "displayName": "example" 4 | } -------------------------------------------------------------------------------- /example/.bundle/config: -------------------------------------------------------------------------------- 1 | BUNDLE_PATH: "vendor/bundle" 2 | BUNDLE_FORCE_RUBY_PLATFORM: 1 3 | -------------------------------------------------------------------------------- /example/common/types/state-type.ts: -------------------------------------------------------------------------------- 1 | export type StateType = { 2 | count?: number; 3 | }; 4 | -------------------------------------------------------------------------------- /img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flagsmith/react-navigation-focus-render/HEAD/img.png -------------------------------------------------------------------------------- /example/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: '@react-native-community', 4 | }; 5 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | example 3 | 4 | -------------------------------------------------------------------------------- /example/ios/example/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /example/android/app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flagsmith/react-navigation-focus-render/HEAD/example/android/app/debug.keystore -------------------------------------------------------------------------------- /example/.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flagsmith/react-navigation-focus-render/HEAD/example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Android/IntelliJ 6 | # 7 | .idea 8 | 9 | # node.js 10 | # 11 | node_modules/ 12 | npm-debug.log 13 | yarn-error.log 14 | -------------------------------------------------------------------------------- /example/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | bracketSpacing: false, 3 | jsxBracketSameLine: true, 4 | singleQuote: true, 5 | trailingComma: 'all', 6 | arrowParens: 'avoid', 7 | }; 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flagsmith/react-navigation-focus-render/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flagsmith/react-navigation-focus-render/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flagsmith/react-navigation-focus-render/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flagsmith/react-navigation-focus-render/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flagsmith/react-navigation-focus-render/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # You may use http://rbenv.org/ or https://rvm.io/ to install and use this version 4 | ruby '2.7.4' 5 | 6 | gem 'cocoapods', '~> 1.11', '>= 1.11.2' 7 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flagsmith/react-navigation-focus-render/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flagsmith/react-navigation-focus-render/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/app/styles/TextStyles.ts: -------------------------------------------------------------------------------- 1 | import {StyleSheet} from 'react-native'; 2 | 3 | export const TextStyles = StyleSheet.create({ 4 | text: { 5 | fontSize: 16, 6 | color: '#333', 7 | }, 8 | }); 9 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flagsmith/react-navigation-focus-render/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flagsmith/react-navigation-focus-render/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flagsmith/react-navigation-focus-render/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/app/styles/ScreenStyles.ts: -------------------------------------------------------------------------------- 1 | import {StyleSheet} from 'react-native'; 2 | 3 | export const ScreenStyles = StyleSheet.create({ 4 | screen: { 5 | backgroundColor: 'white', 6 | flex: 1, 7 | }, 8 | }); 9 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'example' 2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) 3 | include ':app' 4 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export interface MyComponentProps { 4 | Wrapper?: React.FC<{ isFocused:boolean }> 5 | 6 | } 7 | 8 | const ExpensiveComponent: React.FC 9 | export default ExpensiveComponent; 10 | -------------------------------------------------------------------------------- /example/ios/example/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : UIResponder 5 | 6 | @property (nonatomic, strong) UIWindow *window; 7 | 8 | @end 9 | -------------------------------------------------------------------------------- /example/ios/example/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char * argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | 5 | import {AppRegistry} from 'react-native'; 6 | import AppNavigator from './app/components/navigators/AppNavigator'; 7 | import {name as appName} from './app.json'; 8 | 9 | AppRegistry.registerComponent(appName, () => AppNavigator); 10 | -------------------------------------------------------------------------------- /example/ios/example.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/common/app-actions.ts: -------------------------------------------------------------------------------- 1 | import {AnyAction} from 'redux'; 2 | 3 | export const Actions = { 4 | SET_COUNT: 'SET_COUNT', 5 | }; 6 | 7 | // @ts-ignore 8 | export const AppActions = { 9 | setCount(data): AnyAction { 10 | return { 11 | type: Actions.SET_COUNT, 12 | data, 13 | }; 14 | }, 15 | 16 | // END OF APP_ACTIONS 17 | }; 18 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /example/__tests__/App-test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | 5 | import 'react-native'; 6 | import React from 'react'; 7 | import AppNavigator from '../app/components/navigators/AppNavigator'; 8 | 9 | // Note: test renderer must be required after react-native. 10 | import renderer from 'react-test-renderer'; 11 | 12 | it('renders correctly', () => { 13 | renderer.create(); 14 | }); 15 | -------------------------------------------------------------------------------- /example/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | -------------------------------------------------------------------------------- /example/common/reducer.ts: -------------------------------------------------------------------------------- 1 | import produce, {enableES5} from 'immer'; 2 | import {Actions} from './app-actions'; 3 | import {StateType} from './types/state-type'; 4 | enableES5(); // required for react native hermes 5 | 6 | const defaultReducer = produce((state: StateType, action): StateType | void => { 7 | switch (action.type) { 8 | case Actions.SET_COUNT: 9 | state.count = action.data; 10 | return; 11 | default: 12 | break; 13 | } 14 | }, {}); 15 | 16 | export default defaultReducer; 17 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /example/app/components/Text.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {Text, TextProps} from 'react-native'; 3 | import {FC} from 'react'; 4 | import {TextStyles} from 'styles/TextStyles'; 5 | 6 | export type TextType = TextProps & {}; 7 | const TextComponent: FC = props => { 8 | const style = Array.isArray(props.style) ? props.style : [props.style]; 9 | 10 | return ( 11 | 14 | {props.children} 15 | 16 | ); 17 | }; 18 | 19 | export default TextComponent; 20 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/example/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import com.facebook.react.ReactActivity; 4 | import android.os.Bundle; 5 | 6 | public class MainActivity extends ReactActivity { 7 | 8 | @Override 9 | protected void onCreate(Bundle savedInstanceState) { 10 | super.onCreate(null); 11 | } 12 | 13 | /** 14 | * Returns the name of the main component registered from JavaScript. This is used to schedule 15 | * rendering of the component. 16 | */ 17 | @Override 18 | protected String getMainComponentName() { 19 | return "example"; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /example/common/store.ts: -------------------------------------------------------------------------------- 1 | import {createStore, Store} from 'redux'; 2 | import rootReducer from './reducer'; 3 | import {StateType} from './types/state-type'; 4 | 5 | let store: Store & {}; 6 | 7 | export default function _store( 8 | initialState: StateType = {}, 9 | forceNewStore = false, 10 | ) { 11 | // It's very important to only return the cached store on the client, otherwise SSR will return the previous request state 12 | // @ts-ignore 13 | if ( 14 | store && 15 | (typeof window !== 'undefined' || global.__JEST__ !== 'undefined') && 16 | !forceNewStore 17 | ) { 18 | return store; 19 | } 20 | store = createStore(rootReducer, initialState); 21 | return store; 22 | } 23 | -------------------------------------------------------------------------------- /example/android/app/build_defs.bzl: -------------------------------------------------------------------------------- 1 | """Helper definitions to glob .aar and .jar targets""" 2 | 3 | def create_aar_targets(aarfiles): 4 | for aarfile in aarfiles: 5 | name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")] 6 | lib_deps.append(":" + name) 7 | android_prebuilt_aar( 8 | name = name, 9 | aar = aarfile, 10 | ) 11 | 12 | def create_jar_targets(jarfiles): 13 | for jarfile in jarfiles: 14 | name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")] 15 | lib_deps.append(":" + name) 16 | prebuilt_jar( 17 | name = name, 18 | binary_jar = jarfile, 19 | ) 20 | -------------------------------------------------------------------------------- /example/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:metro-react-native-babel-preset'], 3 | plugins: [ 4 | [ 5 | 'module-resolver', 6 | { 7 | root: ['.'], 8 | extensions: [ 9 | '.ios.ts', 10 | '.android.ts', 11 | '.ts', 12 | '.ios.tsx', 13 | '.android.tsx', 14 | '.tsx', 15 | '.jsx', 16 | '.js', 17 | '.json', 18 | ], 19 | alias: { 20 | common: './common', 21 | components: './app/components', 22 | navigators: './app/components/navigators', 23 | screens: './app/components/screens', 24 | styles: './app/styles', 25 | }, 26 | }, 27 | ], 28 | ], 29 | }; 30 | -------------------------------------------------------------------------------- /example/focus.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {useIsFocused} from '@react-navigation/native'; 3 | 4 | class _RenderOnFocus extends React.Component { 5 | state = {}; 6 | 7 | shouldComponentUpdate(nextProps, nextState, nextContext) { 8 | return nextProps.isFocused; 9 | } 10 | 11 | render() { 12 | return <>{this.props.children}; 13 | } 14 | } 15 | 16 | export default function FocusRender({children, Wrapper = null}) { 17 | const isFocused = useIsFocused(); 18 | if (Wrapper) { 19 | return ( 20 | 21 | <_RenderOnFocus isFocused={isFocused}>{children} 22 | 23 | ); 24 | } 25 | return <_RenderOnFocus isFocused={isFocused}>{children}; 26 | } 27 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {useIsFocused} from '@react-navigation/native'; 3 | 4 | class _RenderOnFocus extends React.Component { 5 | state = {}; 6 | 7 | shouldComponentUpdate(nextProps, nextState, nextContext) { 8 | return nextProps.isFocused; 9 | } 10 | 11 | render() { 12 | return <>{this.props.children}; 13 | } 14 | } 15 | 16 | export default function FocusRender({children, Wrapper = null}) { 17 | const isFocused = useIsFocused(); 18 | if (Wrapper) { 19 | return ( 20 | 21 | <_RenderOnFocus isFocused={isFocused}>{children} 22 | 23 | ); 24 | } 25 | return <_RenderOnFocus isFocused={isFocused}>{children}; 26 | } 27 | -------------------------------------------------------------------------------- /example/app/components/screens/Screen2.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {View, Text, Button} from 'react-native'; 3 | import useCount from 'common/hooks/useCount'; 4 | import CustomNavbar from 'components/CustomNavbar'; 5 | import {ScreenStyles} from "styles/ScreenStyles"; // we need this to make JSX compile 6 | 7 | type ComponentType = {}; 8 | 9 | const Screen2: React.FC = ({}) => { 10 | const {setCount, count} = useCount(); 11 | return ( 12 | 13 | 14 | 19 | 20 | ); 21 | }; 22 | 23 | export default Screen2; 24 | -------------------------------------------------------------------------------- /example/ios/example/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /example/ios/exampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | require_relative '../node_modules/react-native/scripts/react_native_pods' 2 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' 3 | 4 | platform :ios, '11.0' 5 | 6 | target 'example' do 7 | config = use_native_modules! 8 | 9 | use_react_native!( 10 | :path => config[:reactNativePath], 11 | # to enable hermes on iOS, change `false` to `true` and then install pods 12 | :hermes_enabled => false 13 | ) 14 | 15 | target 'exampleTests' do 16 | inherit! :complete 17 | # Pods for testing 18 | end 19 | 20 | # Enables Flipper. 21 | # 22 | # Note that if you have use_frameworks! enabled, Flipper will not work and 23 | # you should disable the next line. 24 | use_flipper!() 25 | 26 | post_install do |installer| 27 | react_native_post_install(installer) 28 | __apply_Xcode_12_5_M1_post_install_workaround(installer) 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /example/common/hooks/useCount.ts: -------------------------------------------------------------------------------- 1 | import {useDispatch, useSelector} from 'react-redux'; 2 | import {AppActions} from '../app-actions'; 3 | import {StateType} from '../types/state-type'; 4 | import {useCallback} from 'react'; 5 | 6 | type UseCountActions = { 7 | setCount: (data: number) => void; 8 | }; 9 | 10 | type UseTest = UseCountActions & { 11 | count: StateType['count']; 12 | }; 13 | 14 | export function useCountActions(): UseCountActions { 15 | const dispatch = useDispatch(); 16 | const setCount = useCallback( 17 | (data: number) => { 18 | return dispatch(AppActions.setCount(data)); 19 | }, 20 | [dispatch], 21 | ); 22 | return { 23 | setCount, 24 | }; 25 | } 26 | 27 | export default function useCount(): UseTest { 28 | const {count} = useSelector((state: StateType) => ({ 29 | count: state.count, 30 | })); 31 | const {setCount} = useCountActions(); 32 | return { 33 | count, 34 | setCount, 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "allowSyntheticDefaultImports": true, 5 | "declaration": true, 6 | "esModuleInterop": true, 7 | "allowUmdGlobalAccess": true, 8 | "jsx": "react-native", 9 | "lib": ["es6", "dom"], 10 | "moduleResolution": "node", 11 | "noEmit": true, 12 | "strict": false, 13 | "baseUrl": ".", 14 | "skipLibCheck": true, 15 | "paths": { 16 | "styles/*": [ 17 | "./app/styles/*" 18 | ], 19 | "common/*": [ 20 | "./common/*" 21 | ], 22 | "screens/*": [ 23 | "./app/components/screens/*" 24 | ], 25 | "navigators/*": [ 26 | "./app/components/navigators/*" 27 | ], 28 | "components/*": [ 29 | "./app/components/*" 30 | ], 31 | } 32 | }, 33 | "exclude": [ 34 | "node_modules", 35 | ], 36 | "include": [ 37 | "**/*.ts", 38 | "**/*.tsx" 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 13 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-navigation-focus-render", 3 | "version": "0.0.1", 4 | "description": "Prevents re-renders of components that aren't on the focused screen", 5 | "types": "./index.d.ts", 6 | "main": "./lib/index.js", 7 | "directories": { 8 | "example": "example" 9 | }, 10 | "scripts": { 11 | "test": "echo \"Error: no test specified\" && exit 1" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/Flagsmith/react-navigation-focus-render.git" 16 | }, 17 | "keywords": [ 18 | "react-native", 19 | "performance", 20 | "react-navigation" 21 | ], 22 | "devDependencies": { 23 | "@types/react": "^17.0.39" 24 | }, 25 | "peerDependencies": { 26 | "react": "*", 27 | "react-native": "*", 28 | "@react-navigation/native": "*" 29 | }, 30 | "author": "kyle-ssg", 31 | "license": "ISC", 32 | "bugs": { 33 | "url": "https://github.com/Flagsmith/react-navigation-focus-render/issues" 34 | }, 35 | "homepage": "https://github.com/Flagsmith/react-navigation-focus-render#readme" 36 | } 37 | -------------------------------------------------------------------------------- /example/app/components/navigators/AppNavigator.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {enableScreens} from 'react-native-screens'; 3 | import {NavigationContainer} from '@react-navigation/native'; 4 | import {createNativeStackNavigator} from '@react-navigation/native-stack'; 5 | import HomeScreen from 'screens/HomeScreen'; 6 | import {Provider} from 'react-redux'; 7 | import _store from 'common/store'; 8 | import Screen2 from "screens/Screen2"; 9 | 10 | const store = _store(); 11 | enableScreens(); 12 | const Stack = createNativeStackNavigator(); 13 | 14 | const AppNavigator = function () { 15 | return ( 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | ); 25 | }; 26 | 27 | import performance from 'react-native-performance-monitor/provider'; 28 | export default performance(AppNavigator); 29 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | 24 | # Android/IntelliJ 25 | # 26 | build/ 27 | .idea 28 | .gradle 29 | local.properties 30 | *.iml 31 | *.hprof 32 | 33 | # node.js 34 | # 35 | node_modules/ 36 | npm-debug.log 37 | yarn-error.log 38 | 39 | # BUCK 40 | buck-out/ 41 | \.buckd/ 42 | *.keystore 43 | !debug.keystore 44 | 45 | # fastlane 46 | # 47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 48 | # screenshots whenever they are needed. 49 | # For more information about the recommended setup visit: 50 | # https://docs.fastlane.tools/best-practices/source-control/ 51 | 52 | */fastlane/report.xml 53 | */fastlane/Preview.html 54 | */fastlane/screenshots 55 | 56 | # Bundle artifact 57 | *.jsbundle 58 | 59 | # CocoaPods 60 | /ios/Pods/ 61 | -------------------------------------------------------------------------------- /example/app/components/ExpensiveComponent.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; // we need this to make JSX compile 2 | import Text from 'components/Text'; 3 | import {View} from 'react-native'; 4 | import useCount from 'common/hooks/useCount'; 5 | import FocusRender from '../../../lib/index'; // we need this to make JSX compile 6 | type ComponentType = { 7 | useFocusRender: boolean; 8 | }; 9 | 10 | 11 | const Wrapper = ({isFocused, children}) => ( 12 | {children} 13 | ); 14 | 15 | const ExpensiveComponent: React.FC = ({useFocusRender}) => { 16 | const {count} = useCount(); 17 | return useFocusRender ? ( 18 | 19 | {!!count && Count is {count}} 20 | {new Array(30).fill(0).map((v, k) => ( 21 | {v} 22 | ))} 23 | 24 | ) : ( 25 | <> 26 | {!!count && Count is {count}} 27 | {new Array(30).fill(0).map((v, k) => ( 28 | {v} 29 | ))} 30 | 31 | ); 32 | }; 33 | 34 | export default ExpensiveComponent; 35 | -------------------------------------------------------------------------------- /example/app/components/screens/HomeScreen.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {View, Button} from 'react-native'; 3 | import {useState} from 'react'; 4 | import {ScreenStyles} from 'styles/ScreenStyles'; 5 | import ExpensiveComponent from '../ExpensiveComponent'; 6 | import {useNavigation} from '@react-navigation/core'; 7 | import CustomNavbar from 'components/CustomNavbar'; // we need this to make JSX compile 8 | type ComponentType = {}; 9 | 10 | const HomeScreen: React.FC = ({}) => { 11 | const [isActive, setIsActive] = useState(false); 12 | const navigation = useNavigation(); 13 | return ( 14 | 15 | 16 |