React admin: Confirm message on route switch - react-admin

I want to prevent users from leaving CreateVacancy component by warning them about unsaved changes.
Dealing with ##router/LOCATION_CHANGE via redux saga is of little help, because action is already dispatched and page switch will happen anyways.
Difficulty with React Admin is that I don't have access to <Route/> component directly. Otherwise I would use Route's onLeave prop to achieve my goal.
I need to somehow track previous location (/vacancy/create) and prevent users from leaving to any other route without confirming.
What would you recommend? Thanks.

I think this can be a great feature request. Can you create one on our repository? In the mean time, as we use react-router, you can probably leverage its Prompt component:
import { Prompt } from 'react-router';
const PostCreate = props => (
<Fragment>
<Prompt message="Are you sure you want to leave?" />
<Create {...props}>
...
</Create>
</Fragment>
)
I haven't tested it but it should work. If you need to customize the dialog further, have a look at this article

Related

React Native: Proper way to handle huge forms

I have an application developed for internal users. The home page of the app has at least 30 input fields.
<TextInput onChangeText={onChangeNumber} value={number}/>
I have onchangeText for all the 30 inputs, in the future I might add more fields. I don't think adding onchange to all the fields is the best way to do it. How can I optimize the approach? Are there any 3rd party packages where it doesn't re-render for every input change and only captures data on submit?
the way I handle large forms is this,
Store your form data in an object using useState like this,
const [formData, setFormData] = useState({name: "", age:""})
you can pass "onChangeText" like this,
<TextInput
value={formData.name}
onChangeText={value => setFormData(prev => { return { ...prev, name: value } })}
/>
I would suggest creating a separate component for "TextInput", so you can also handle validations in it.
Try using onEndEditing, as per docs:
onEndEditing
Callback that is called when text input ends.
So just changing to
<TextInput onEndEditing={onChangeNumber} value={number}/> should do
There are a few other alternatives on the docs, you might check to see the one that fits you better.
https://reactnative.dev/docs/textinput#onendediting
You should try formik + yup.
I am using it in several projects and this is the best way to manage little and big forms!

open route in a sidepage using vuejs

I'm building a back office using Quasar. I would like to be able to open routes in what I would call a sidepage (for the lack of a better better word... maybe you have one?).
Opening a route in this sidepage would render the component requested without decoration (like menus).
This route could potentially however be opened directly in a new window (not in a sidepage) with decoration.
From this sidepage, I would like to be able to open another sidepage, and so on.
This is basically what I'd like to do (https://tagmanager.google.com/) :
I've literally no clue how to do that. I'm trying to build a component that would take a route, create a sidepage, size it correctly (as you can see the second sidepage is smaller than the first one) )and open the route in it but I fail to do so.
Any clue where to start or any idea on a component that would do that already ? I haven't found anything yet.
You could search for your component using the path in the router options.
const route = this.$router.options.routes.find(
(route) => !!route?.path?.includes("route/to/display")
)
this.component = route?.component;
And then display it using component tag.
<component :is="component" />

How can I remove manually added event listeners on beforeDestory hook of one component before redirecting to any another component, in Vuejs2?

I want to know the concept of the below thing-
I have created one component and set up its respected event listeners. Now, I want to remove those listeners on this component's beforeDestroy hook before redirecting to another route that will create another component.
but what I noticed is, beforeDestory hook of the first component is calling even after the second component's created hook.
I want to destroy the first component completely and then create another component.
// To set up the event listeners
created() {
this.EventBus.$on('myCustomEvent', payload => {
// some code here
)}
}
// To destroy the event listeners
beforeDestroy() {
this.EventBus.$off('myCustomEvent');
}
Any suggestions?
In search of an answer to your question, I came to the conclusion that it is better to refuse to use EventBus altogether.
Here is some information on this and some thoughts from there:
I have that feeling that having an EventBus in Vue is an anti-pattern, especially if you’re using VueX but I can’t quite put my finger on it. At the point you want to be sharing data like that, wouldn’t it be better to use a store to handle all of those “events” / “mutations”?
Also, looking at the solution to this issue, you are doing everything right and there is no other way.

Prevent loss of state when unmounting view

My app uses a react-navigation DrawerNavigator component to allow the user to navigate through various screens within the app.
My react-native-maps MapView component is nested inside a screen accessible via the DrawerNavigator.
The problem I am finding is that if you navigate to another page in the app, and then navigate back to the map page, the whole map has to reload and previous markers/map configuration is lost.
Is there a way that I can prevent the screen from unmounting when navigating away, or another way of stopping the whole map from resetting? I won't post code below because I believe the issue to be more theory based as opposed to fixing a code bug.
You need to persist the state when the component is unmounted. You need a state management library.
I know of two state management libraries.
RxJS is the recommended library for use with Angular. Even though it is not an developed by Angular, it is still installed by default if you use the Angular CLI to bootstrap a project. This library is incredibly powerful, especially with handling asynchronous data flows, and it fits in really well with the angular DI system. My understanding is that you create singleton services to manage particular parts of your global state. You could have many RxJS services for different parts of your app. Your components can then tap into these services and get state information from them. There are libraries which help you integrate RxJS with react components but I cannot attest to their value.
Redux is the canonical way to manage global and persisted state in React. It differs from RxJS in many ways. First, you have only one redux store in your whole app and it contains the entire global state. Second, Redux is modeled on Flux and setting up the various 'players' for the first time can be a very involved process (but once you get it it's easy). I highly recommend making use of the combineReducers function to simplify getting set up. Third, redux does not manage async data straight out of the box, you will need to reach for redux-thunkif you have async data flows.
Redux is still my go-to for global and persisted state in react because of how it integrates. There is a library called react-redux which integrates the two libraries really well. It provides you with a function called connect. The connect function accesses your global state and passes it into your components as a prop.
You wrap your entire app in a store provider line so
export default () => {
<Provider store={store}>
<App />
</Provider>
Then your individual components can access state using connect. connect accepts a function which extracts parts of your state for you. The function could look like this.
const mapStateToProps = state => {
return {
stateVariable: state.variable
}
Now you know your component will receive a prop called stateVariable which is the value of variable in your global store / state. So you can write your component to accept this prop
class Component extends React.Component {
render() {
var { stateVariable} = this.props;
return (
<View>
<Text>{stateVariable}</Text>
</View>
)
}
Then you call connect on your component with the mapStateToProps function and hey presto
const ConnectedComponent = connect(mapStateToProps)(Component)
export { ConnectedComponent as Component }
You see how this injects the props as if you had written
<Component stateVariable={state.variable} />
In this way it is a solution to prop-drilling
In addition, you can use redux-persist to persist state between sessions, not just mounting/unmounting components. This library accesses localStorage on web or asyncStorage on native.
When you call connect on a component is automatically passes in a prop called dispatch. Dispatch is a function which is used to dispatch actions which make edits to your local store. as I said the system requires some setting up - you must create constants, actions-creators, and reducers to manage these action dispatches. If you watch the first 8 videos of this course you will be well on your way https://egghead.io/courses/getting-started-with-redux
At this moment in time my recommendation is to use Redux with React.

Props not propagating to children

I'm working on adding swipe to remove functionality to an app we are developing. For reasons we are not using an external library to handle this, so I am writing it myself.
In my project I have a container where I keep state. I use setState to update the state, and am passing state down to this child component as a prop. In the component below, componentWillReceiveProps is called with the correct value updates when they happen, but the child component of this is not receiving updates to its props. If this doesn't make enough sense or you need to see more code let me know. I've only included the parts of code that I feel are relevant since this is a private project.
constructor(props) {
super(props);
this.renderWishlistRow = this.renderWishlistRow.bind(this);
}
renderWishlistRow(product) {
return (
<WishlistRow
item={product}
onItemPress={this.props.onItemPress}
onRemoveAction={this.props.onRemoveAction}
shouldCloseRemoveButton={this.props.shouldCloseRemoveButton}
onScrollAction={this.props.onScrollAction}
itemPressed={this.props.itemPressed}
onEndScroll={this.props.onEndScroll}
/>
);
}
Then, inside the render function:
return (
<KeyboardAwareListView
dataSource={this.props.dataSource}
renderRow={this.renderWishlistRow}
renderSeparator={renderCardDividerAsSeparator}
onScrollBeginDrag={this.props.onScrollAction}
scrollEventThrottle={16}
onScrollEndDrag={this.props.onEndScroll}
/>
);
Thanks in advance for any help.
EDIT:
I am setting state in the parent component with this code:
this.setState({
shouldCloseRemoveButton: true,
});
I didn't originally include it because componentWillReceiveProps is being called with the correct state changes from the parent component.
EDIT 2:
My App Hierarchy for this part of the app is as follows:
WishlistContainer: contains the setState calls and passes as a prop: shouldCloseRemoveButton={this.state.shouldCloseRemoveButton}
Wishlist: passes props to its child WishlistRow: shouldCloseRemoveButton={this.props.shouldCloseRemoveButton}
WishlistRow: Continues to pass the props down as above, but componentWillReceiveProps is not called here, props are not updating at this level.
I'm not going to mark this as answered, because I want a real answer and what I did to work around this is not good enough for me.
That being said, my workaround was to move the piece of state I was trying to propagate into react-redux. Setting the redux state to contain what I needed using mapDispatchToProps, and then connecting the components that actually needed the state down the line using mapStateToProps, allows the components to receive the notifications they need to do their thing.
Again, I am not choosing this as the answer - even though it is what I did to solve the problem - because something else fishy is going on somewhere and I would like to see if anyone knows why things didn't work as they were.
EDIT
I've run into this issue other times since this originally happened. There is a prop that exists on the Flatlist - This is not the original component I used in the question, but the original component is deprecated now and the Flatlist has the, about to be mentioned, prop for this scenario - called extraData. This particular prop is also watched to help the Flatlist determine if it should rerender itself.
Since the ListView has become deprecated, I feel that using a Flatlist and making sure you pass in an extraData prop - assuming you have a different prop that will change with your list data changes - is an acceptable answer to this problem.