Skip to content
This repository has been archived by the owner on Dec 3, 2022. It is now read-only.

Refine use navigation param #20

Closed
wants to merge 7 commits into from
Closed

Refine use navigation param #20

wants to merge 7 commits into from

Conversation

benseitz
Copy link
Contributor

@benseitz benseitz commented May 27, 2019

Introduces a useState like API for useNavigationParam as suggested in #19 and allows a fallback value as a second argument.

Theres also an updated and one added test as well as updated documentation and a new warning because of this breaking changes.

Currently the linter is failing because of a missing type for fallbackValue.
Since I'm not very fluent in typescript I'd couldn't find a solution that doesn't end in errors :/

@FireyFly
Copy link

Very neat! I'm not particularly well-versed in typescript either, so you probably tried this, but I would expect fallbackValue?: NavigationParams[T] (or something along those lines) to work.

Though, digging a bit to see what the type of getParam itself is, it seems the ts typings for it defines it as an overloaded function, with a single-argument case returning a nullable result, and with the two-argument case enforcing the result to be non-nullable.

Honestly, I wouldn't worry too much about the fallback value param--it does make sense I think to support it, since it matches what getParam does, but I think it's much less of a hassle to add later on compared to the "return pair of value and setter" change. Like, it's nice if we find a typing that works for it, but it's not the end of the world if we skip the fallback-value param for now.

Thanks a lot for writing up this PR btw! 😄

README.md Outdated Show resolved Hide resolved
src/Hooks.ts Outdated Show resolved Hide resolved
src/Hooks.ts Outdated Show resolved Hide resolved
src/Hooks.ts Outdated
const { getParam, setParams } = useNavigation()
return [
getParam(paramName, fallbackValue),
(newValue: NonNullable<NavigationParams[T]>) => setParams({ [paramName]: newValue }),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe worth wrapping this in React.useCallback to preserve the reference?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which part exactly?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function which is used to update the param

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if it's that much of a performance difference to just memoize the reference to that function.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not about performance optimisation due to memoizing, but it will avoid extra step to memoize it if you are using React.memo or React.PureComponent. iirc, the updater function from React.useState preserves the reference for this reason

@benseitz
Copy link
Contributor Author

benseitz commented May 28, 2019

@satya164 Thank you for the review! I pushed some changes, that resolve most of the change requests and the failing linter :)

src/Hooks.ts Outdated
const { getParam, setParams } = useNavigation()
return [
fallbackValue ? getParam(paramName, fallbackValue) : getParam(paramName),
(newValue: NavigationParams[T]) => setParams({ [paramName]: newValue }),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with @satya164, need useCallback here

const setParamValue = useCallback(() => {
   return (newValue: NavigationParams[T]) => setParams({ [paramName]: newValue });
},[setParams]);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay - I just implemented it like you suggested :)

@slorber
Copy link
Member

slorber commented Sep 20, 2019

Thanks @benseitz , that looks fine to me

Can you rebase and resolve the conflicts?

I'm not going to merge it very soon because it's a breaking chance, will try to see what other breaking changes we can do for a v2

@slorber
Copy link
Member

slorber commented Sep 30, 2019

@satya164 do you plan to include such hook in react-navigation v5?

As mentionned here: #44
I think it's not a good idea to add hooks here that won't be in v5, will only make the migration more painful, and I currently don't see such a hook in v5 codebase)

@satya164
Copy link
Member

satya164 commented Sep 30, 2019

@slorber we are not, in v5 the route object is a top level prop, so instead of navigation.state.params, user can do route.params which is shorter.

For handling optional params and default value, the upcoming optional chaining and nullish coalescing operators work well:

- useNavigation().getParam('foo, 'defaultValue')
+ useRoute().params?.foo ?? 'defaultValue'

We do have a useRoute hook similar to useNavigation since the route isn't part of the navigation object anymore.

@slorber
Copy link
Member

slorber commented Oct 1, 2019

so, @satya164 do you think we should merge it? I think it's not a big deal even if v5 does not have it, because we already have useNavigationParam() anyway. The other option would be to remove it and ask user to use optional chaining, but not sure many have enabled it on their babel setup (TS just merged the PR)

@satya164
Copy link
Member

satya164 commented Oct 1, 2019

Regarding optional chaining, React Navigation v5 is still in alpha, so I'm not much worried about it not being available by default yet.

Regarding whether to merge the PR, it's upto you. Though a breaking change for this when upgrading to hooks v2 and again when upgrading to navigation v5 might not be best.

Copy link

@CyxouD CyxouD left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your work 👍 Added fix for "Maximum update depth exceeded" crash and left suggestion to make Typescript users' life better :)

Comment on lines 18 to 34
export function useNavigationParam<T extends keyof NavigationParams>(
paramName: T
paramName: T,
fallbackValue?: NavigationParams[T]
) {
return useNavigation().getParam(paramName);
const { getParam, setParams } = useNavigation();
const setParamValue = useCallback(
() => {
return (newValue: NavigationParams[T]) =>
setParams({ [paramName]: newValue });
},
[setParams]
);
return [
fallbackValue ? getParam(paramName, fallbackValue) : getParam(paramName),
setParamValue(),
];
}
Copy link

@CyxouD CyxouD Nov 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

export default function useNavigationParam<
  T extends keyof Params,
  Params extends NavigationParams = NavigationParams
>(
  paramName: T,
  fallback?: Params[T],
): [Params[T], (value: Params[T]) => boolean] {
  const { getParam, setParams } = useNavigation();
  const setParamValue = useCallback(
    (newValue: Params[T]) => setParams({ [paramName]: newValue }),
    // `setParams` function is not memoized, so that it causes "Maximum update depth exceeded" (https://github.com/react-navigation/core/issues/71)
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [paramName],
  );

  // had to cast `paramName` because `navigation.getParam` thinks that some other type can be passed
  return [getParam(paramName as string, fallback), setParamValue];
}

I would suggest these change where I added additional optional type Params for NavigationParams, so that you can optionally determine actual types of keys' values instead of seeing any (it is made possible with Index types http://www.typescriptlang.org/docs/handbook/advanced-types.html#index-types). There is case when type is misleading, fallback value will always be shown as possibly undefined even if it is required in Params.
Also fixed "Maximum update depth exceeded" which described in suggestion below and added paramName to deps as ESlint suggested

Copy link

@CyxouD CyxouD left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix "Maximum update depth exceeded" crash in current implementation

Comment on lines +23 to +33
const setParamValue = useCallback(
() => {
return (newValue: NavigationParams[T]) =>
setParams({ [paramName]: newValue });
},
[setParams]
);
return [
fallbackValue ? getParam(paramName, fallbackValue) : getParam(paramName),
setParamValue(),
];
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both setParams in deps of useCallback (https://github.com/react-navigation/core/issues/71) and redundant function wrapping causes crash with "Maximum update depth exceeded" error in my React-Native app, so suggest to avoid it. Also added paramName prop as ESLint suggested

const setParamValue = useCallback(
    (newValue: NavigationParams[T]) => setParams({ [paramName]: newValue }),
    // `setParams` function is not memoized, so that it causes "Maximum update depth exceeded" (https://github.com/react-navigation/core/issues/71)
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [paramName],
  );
  return [
    fallbackValue ? getParam(paramName, fallbackValue) : getParam(paramName),
    setParamValue,
  ];

@slorber
Copy link
Member

slorber commented Feb 3, 2020

Hi,

I think we'd better not ship this feature if this is not in v5

Thanks

@slorber slorber closed this Feb 3, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants