Calling LoadingIndicator as required - react-admin

I would like to call the LoadingIndicator or a busy indicator during a process so that the user cannot navigate away while the process is in progress.
I cannot find anything in the documentation on how to do this.

In react-admin, the loading indicator reacts to custom Redux actions. If you want to start it, you can dispatch them manyally:
import { useDispatch } from 'react-redux';
import { fetchStart, fetchEnd } from 'react-admin';
const MyComponent = () => {
const dispatch = useDispatch();
const startLoader = () => {
dispatch(fetchStart());
}
const endLoader = () => {
dispatch(fetchEnd());
}
return (/* ...*/);
}
However, this doesn't block user navigation. If you want to block users, you should use a material-ui Dialog.

Related

useEffect (with depencies from redux useSelector hooks) into custom hooks it's trigger on every import

I'm new in react native world and i'm on a new project with store (manage by redux).
I encounter an issue with custom hooks and useEffect
here my custom hooks
const useTheme = () => {
const [activeTheme, setActiveTheme] = useState();
const { id: universID, defaultTheme: universDefaultTheme } = useSelector(
(state) => state.univers
);
const { theme } = useSelector((state) => state);
const { themes: activeThemes } = useSelector((state) => state.settings);
const dispatch = useDispatch();
//set theme when univers change
useEffect(() => {
console.log('TODO TOO MANY CALLS!!!!!', universID);
if (universID) {
setTheme(
activeThemes.find((theme) => theme.univers === universID)?.theme
);
}
}, [universID]);
//get active theme of current univers
useEffect(() => {
setActiveTheme(
activeThemes.find((theme) => theme.univers === universID)?.theme
);
}, [activeThemes]);
... rest of code ...
return {
theme,
activeTheme,
setTheme,
};
}
on components i use
const {
theme: { colors },
} = useTheme();
My issue is that on every import the useEffect(()=>{},[universID]) is trigger. UniversID come from redux store.
If i understand clearly when i import useTheme() the reference of universID change because there are copy of universID from store created, and reference change.
if i pass universID as arguments to useTheme hooks there are no problem, cause reference is the same. But if i do this i need tu make a useSelector(universID) on every components who import useTheme hooks.
My understanding of mecanism is good ?
There are a way to get universID from store with the same reference on every import, for not trigger useEffect(,[universID]) on every import ? without pass universID as arguments of useTheme (i.e. useRef, useCallback) ?
Thanks for the time past to read (or better, to answer ;))

Error: Actions must be plain objects. Use custom middleware for async actions. (React Native)

I am trying to make it so that if an item called code is not set in state with redux, it is called from AsyncStorage and state is set.
import {AsyncStorage} from 'react-native'
import { connect } from 'react-redux';
import {bindActionCreators} from 'redux'
import {handlePhoneNumber, saveCode} from './../../actions/RegistrationActions';
class EnterPhoneNumberScreen extends React.Component {
componentDidMount() {
let code = this.props.registration.code;
console.log("code is", code);
if(code){
// Do nothing
}else{
console.log("in the else");
this.props.getAndSetCode();
}
}
}
const getAndSetCode = () => dispatch => {
console.log("in get and set Code");
AsyncStorage.getItem('code')
.then((data) => {
console.log("data is ", data);
dispatch(saveCode(data));
console.log("in getAndSetCode method, Code is ", data);
})
}
const mapDispatchToProps = dispatch => (
bindActionCreators({
handlePhoneNumber,
getAndSetCode: () => dispatch(getAndSetCode()),
}, dispatch)
);
export default connect(mapStateToProps, mapDispatchToProps)(EnterPhoneNumberScreen);
The console outputs the following:
LOG code is null
LOG in the else
LOG in get and set Code
LOG data is 3tgvgq
LOG in getAndSetCode method, Code is 3tgvgq
I know thunk is properly installed because it is running elsewhere in the application. saveCode is just a normal action:
export const saveCode = code => ({
type: "SAVE_CODE",
payload: code
})
And this error appears in the iphone11 simulator:
How do I fix this?

How to use useFocusEffect hook

As the docs https://reactnavigation.org/docs/en/next/use-focus-effect.html,
"Sometimes we want to run side-effects when a screen is focused. A side effect may involve things like adding an event listener, fetching data, updating document title, etc."
I'm trying to use useFocusEffect to fetch data everytime that the user go to that page.
on my component I have a function which dispatch an action with redux to fetch the data:
const fetchData = ()=>{
dispatch(companyJobsFetch(userDetails.companyId));
};
Actually I'm using useEffect hook to call fetchData(), but I'd like to fetch data everytime that the user go to that page and not only when rendered the first time.
It's not clear from the documentation how to use useFocusEffect and I'm not having success on how to do it.
Any help?
The docs show you how to do it. You need to replace API.subscribe with your own thing:
useFocusEffect(
React.useCallback(() => {
dispatch(companyJobsFetch(userDetails.companyId));
}, [dispatch, companyJobsFetch, userDetails.companyId])
);
For version react navigation 4.x, you can use addEvent listener
useEffect(() => {
if (navigation.isFocused()) {
resetReviews(); // replace with your function
}
}, [navigation.isFocused()]);
OR
useEffect(() => {
const focusListener = navigation.addListener('didFocus', () => {
// The screen is focused
// Call any action
_getBusiness({id: business?.id}); // replace with your function
});
return () => {
// clean up event listener
focusListener.remove();
};
}, []);
For later version 5.x, you can use hooks to achieve this
import { useIsFocused } from '#react-navigation/native';
// ...
function Profile() {
const isFocused = useIsFocused();
return <Text>{isFocused ? 'focused' : 'unfocused'}</Text>;
}

How to set config show/hide refresh button on AppBar

Click to see image
Button refresh on AppBar is not refresh on page Dashboard because I just use Component Card but work on page using component List or Datagrid, so I want to config show/hide refresh button on AppBar or how to fix it work for page not use component List or Datagrid.
Sorry I'm not strong in English.
You'll have to fetch some data from the react-admin state for it to work. Indeed, the refresh button just trigger the refreshView action which update the state.admin.ui.viewVersion key of the the react-admin redux state. This key is a simple counter. Internally, we use this counter to check whether we must update some components data. Here is a simple example of a connected Dashboard which can do things when refreshed:
import React, { Component } from "react";
import { connect } from "react-redux";
class Dashboard extends Component {
componentDidMount() {
this.doOnMountAndWhenRefreshed();
}
componentDidUpdate(prevProps) {
if (prevProps.views !== this.props.views) {
this.doOnMountAndWhenRefreshed();
}
}
doOnMountAndWhenRefreshed = () => {
// This is where you do update your component:
// - Make API requests
// - Fetch data from the react-admin store, etc.
};
render() {
const { views } = this.props;
return <div>Refreshed {views} times.</div>;
}
}
const mapStateToProps = state => ({ views: state.admin.ui.viewVersion });
export default connect(
mapStateToProps,
{}
)(Dashboard);
You can see it working in this codesandbox
Edit for newer version of react-admin
import { useVersion } from 'react-admin';
const Dashboard = () => {
const version = useVersion();
return <div>Refreshed {version} times.</div>;
}
In react-admin 4.x I managed to get the desired behaviour like this:
import React from 'react'
import { useQuery } from 'react-query'
const noop = async () => new Date().valueOf()
export const MyDashboard = () => {
const { data } = useQuery('myDashboard', noop)
return (
<div>Last refreshed at {data}</div>
)
}
export default MyDashboard
Note how data represents the value returned by noop().
That way, whenever the user presses the refresh icon in the AppBar, the component is re-rendered.

How to use a custom reducer's state as a permanent filter in a <List>?

I have a custom reducer and a connected component to change its state. Now I'd like to use this state as a permanent filter on List elements.
I understand the List elements are connected to the redux-state, so I hope I'm able to access it through the List component's props, but couldn't find a way how to do that.
The List component is connected but not yours.
import { connect } from "react-redux";
const MyList = ({ is_published, ...props }) => (
<List {...props} filter={{ is_published }}>
</List>
);
const mapStateToProps = state => ({
is_published: state.myCustomReducer.is_published,
});
export default connect(mapStateToProps, undefined)(MyList);
Edit:
Just found out we don't update data when this prop change. This is a bug and you can open an issue about it.
In the mean time, here's a workaround:
Create a custom saga listening to whatever action you use alongside your custom reducer (I'll call it SET_IS_PUBLISHED for my example). This custom saga should put the changeListParams action creator from react-admin with your filter.
It will probably looks like this (not tested):
import { takeEvery, put, select } from 'redux-saga/effects'
import { changeListParams } from 'react-admin'
import { SET_IS_PUBLISHED } from './isPublished'
const getCurrentListParams = (state, resource) => {
const resourceState = state.admin.resources[resource]
return resourceState.list.params
}
function handleSetPublished({ payload }) {
const currentParams = yield select(getCurrentListParams)
const newParams = {
// Keep the current params
...currentParams,
// Override the filter
filter: {
// Keep the current filter
...currentParams.filter,
// Only override the is_published
is_published: payload
}
}
// Dispatch the action for the `posts` resource
yield put(changeListParams('posts', newParams))
}
export default function* () {
yield takeEvery(SET_IS_PUBLISHED, handleSetPublished)
}
just to bring this into 2021, you can use the useSelector redux hook to get hold of your custom state:
import { useSelector } from 'react-redux';
const MyCustomThing = (props) => {
const is_published = useSelector(state => state.customState.is_published);
}
For completeness, react-admin provides a customReducers prop to its <Admin> component so you can extend the redux state with your custom values:
const customStateReducer = (customState = { is_published: false }, { type, payload }) => {
if (type === 'IS_PUBLISHED') customState.is_published = payload.is_published;
return customState;
}
<Admin customReducers={{ customState: customStateReducer }} ...>
etc