-
-
Notifications
You must be signed in to change notification settings - Fork 5.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Best pattern for a 'Save' button in the header #145
Comments
On a somewhat related note, Ive seen references to a Slack channel. That would be a better place for this discussion but I'm unsure of how to get access to that channel. |
There is some discussion on the reactiflux discord server (https://www.reactiflux.com/), specifically in the rn-navigation channel, although I don't think its specifically for this library. |
Related: #149 |
the only solution I can think of is using |
Thanks @Schniz . Thats the pattern I was thinking as well and the path that I ultimately took. Unfortunately I ran into what appears to be a bug related to calling setParams. Ive opened a separate issue for that bug. All in all, this pattern seems a bit hazy and not entirely 'safe' unless you explicitly check for the existence of all state.params. |
Yeah. I think that |
However, I really dig using ramda's import { pathOr } from 'ramda';
const noop = () => {};
// ...
const somethingToDoFn = pathOr(noop, ['params', 'somethingToDoFn'], navigator);
somethingToDoFn('data', 'to', 'pass'); |
Isn't the proper way to do this: componentDidMount () {
const isDisabled = <someLogicUsingLocalState>;
const params = {
right: (
<Button
onPress={() =>!isDisabled ? this.onSave() : null}
>
<Text style={[styles.rightButtonTextStyle, isDisabled ? styles.disabledStyle : null]}>
Save
</Text>
</Button>
),
};
this.props.navigation.setParams(params);
} However, #160 is preventing this from working well. |
Thought I would post my main navigation options too for completness sake: function getCurrentParams (state) {
if (state.routes) {
return getCurrentParams(state.routes[state.index]);
}
return state.params || {};
}
...some setup...
// main stack navigator navigationOptions
navigationOptions: {
header: ({state}) => {
// get the "deepest" current params.
const currentParams = getCurrentParams(state);
const left = currentParams.left;
const right = currentParams.right;
const style = currentParams.style;
const tintColor = currentParams.tintColor;
return { left, right, style, tintColor };
}
} This feels kinda hacky to me, but I don't see another way to set dynamic header properties other than the use of setParams (and again, #160 makes this a poor solution at the moment). |
If people find that useful then I'll make a pr here in the next couple of days, and we can standardize/critique it there. |
@dmhood solution works great. I was looking to show a modal view when pressing one of the header buttons (left or right). Thanks! |
FWIW using setParams in componentDidMount is apparently discouraged, (see #296). I've mostly gone to pulling most of the "initial state" logic to set header buttons out into static/helper functions, which isn't great but should be doable unless your component uses something external like react-context. |
I'm not sure I understood @dmhood's solution. How do I call the class MyScreen extends React.Component {
static navigationOptions = {
header: {
right: <Button title={"Save"} onPress={() => this.saveDetails()} />
}
};
saveDetails() {
alert('Save Details');
}
render() {
return (
<View />
);
}
} |
@raymundtusk7 you can't call this.saveDetails() from a static context since the actual saveDetails method is a class method. You would need to make saveDetails static or just some helper function outside of the class. |
Maybe all the functions (when declaring |
Can't seem to wrap my head around this. If I make the Another scenario: |
Try setting your component instance's class MyScreen extends React.Component {
static navigationOptions = {
header: ({ state }) => ({
right: <Button title={"Save"} onPress={state.params.handleSave} />
})
};
saveDetails() {
alert('Save Details');
}
componentDidMount() {
this.props.navigation.setParams({ handleSave: this.saveDetails });
}
render() {
return (
<View />
);
}
} |
@stevehollaar: Awesome! This worked for me though: <Button title={"Save"} onPress={() => {state.params.handleSave()}} /> But you pointed me to the right direction. Thanks a lot! 💯 |
Is there a way to dynamically enable or disable it? Meaning, if the user changes input the |
@stief510 yes, please see |
@grabbou But is there a way to dynamically do so from the component itself? I know you can change it manually, like if the user presses something. But what if the we automatically detect that the user filled out the whole form? Can I automatically enable the button somehow? |
I'm playing with something which might solve your issues https://github.com/satya164/react-navigation-addons This is just an exploration. Feedback welcome. :D |
@satya164 Looks exactly like what I need. I'll check it out |
@fqborges I used your solution and works great. I just have a quick question. There's a bit of delay before the button is rendered on screen, so the default button of react-navigation is rendered first and is then replaced with the custom button on your solution. Is there anyway that the custom button is rendered first or before the component mounts? |
@Nitro2003 That's because the header params are only set after component mount. Take a look at the docs here, they explain how to avoid it. |
@a88zach alternatively you could call onTextChange = (text) => {
this.setState({
theText: text,
});
this.props.navigation.setParams({
hasErrors: this.hasErrors(),
});
} Just note that |
non of the above work now. WHY? the best answer code as below.
this will do an infinity render. so can't work. the BEST of THE BEST solution is create your own header and customise yourself.
use react navigation as a tool to change screen only. that it. |
@kkusanagi It wont infinitely render. Setting navigation params does not trigger a component update. The code in the docs works and has been tested numerous times. |
@Jacse thanks for the remind. I miss checking the link added above. https://reactnavigation.org/docs/intro/headers#Header-interaction-with-screen-component Anyway, I also give up on using react navigation header. Cause in my case, I need to fully handle header like changing background and animated hide the header. |
Hi Guys, I must say set action through the |
@kkusanagi That is because the binding. With fat arrow functions you get the correct binding in this case. |
@nonameolsson I used fat arrow functions for it as this document. And it causes the slow transition. |
I've to check if the
|
My Solution:
|
This is what worked for me: `componentDidMount() { modal() { static navigationOptions = ({ navigation }) => ({ |
None of these works for me static navigationOptions = ({ navigation }) => ({
headerRight: <Button onPress={() => navigation.state.params.updatedUser} />,
})
componentDidMount() {
this.props.navigation.setParams({ updateUser: this.updateUser })
}
updateUser = () => {
console.log('user updated')
} |
@corasan you are passing
While based on your code, you should be passing
to Button.onPress |
@mgtitimoli if I do that I immediately get an error, before the view even renders Edit: Fixed it, thank you! You actually pointed me in the right direction to find out what I was doing wrong exactly |
@corasan What was the correct fix? |
@betiol How can I do that if instead the |
@ rajat1saxena i also received this.props.navigation.state.params is undefined error. I realized that it is because i have the stack navigator inside the tab navigator. So the state i am looking at is the top navigator state. Try see if there is a routes array, this.props.navigation.state.routes. Your params may be in there. |
@a88zach’s solution worked best for me to keep state and params in "sync", #145 (comment). |
Hi, I'm has a drawer (StackNavigator) with header, inside the root navigation (SwitchNavigator) without header. Like this: Root (SwitchNavigator) How I'm change this header/navigation info in drawer (StackNavigator) navigationOption? |
Similar to @benjaminreid -- I ended up taking @a88zach's approach and created a HOC to make it a little less work and ugly. |
Very concerning a feature like this requires so much workaround In my case, pressing the header button before componentDidMount also caused an error. Had to resort to something like this
|
@jrhee17 - I don't know what you're trying to get at by saying it's "very concerning" - please use objective language when discussing issues so it's easier to be actionable. I agree this API isn't ideal, we should use an event emitter, see react-navigation/rfcs#48 |
I should also mention in the above example you don't actually need to provide a noop function, you could just do |
Often times if you are editing or creating something, one of the header buttons will be 'Save'. I don't see how this is feasible with this library since there is no access to the state or props of the current screen component. The only (ugly) solution would be to keep the data to be saved outside of the component. Anyone have any suggestions how this pattern can be achieved with this library?
The text was updated successfully, but these errors were encountered: