How can i disable the auto-refreshing feature of react-admin - javascript

So react-admin seems to have a feature where if you're idle for a little while and come back it will reload the data, presumably to make sure you're looking at the most up to date version of a record.
This is causing some issues for my editing feature that has some custom components. Is there a way to disable this auto-reload feature?

The auto-refresh is triggered by the loading indicator (the spinner icon that you see in the top right part of the app bar).
You can disable the auto-refresh by replacing the loading indicator by your own.
import * as React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { useSelector } from 'react-redux';
import { makeStyles } from '#material-ui/core/styles';
import CircularProgress from '#material-ui/core/CircularProgress';
import { useRefreshWhenVisible, RefreshIconButton } from 'react-admin';
const useStyles = makeStyles(
{
loader: {
margin: 14,
},
loadedIcon: {},
},
{ name: 'RaLoadingIndicator' }
);
const LoadingIndicator = props => {
const { classes: classesOverride, className, ...rest } = props;
useRefreshWhenVisible(); // <= comment this line to disable auto-refresh
const loading = useSelector(state => state.admin.loading > 0);
const classes = useStyles(props);
return loading ? (
<CircularProgress
className={classNames('app-loader', classes.loader, className)}
color="inherit"
size={18}
thickness={5}
{...rest}
/>
) : (
<RefreshIconButton className={classes.loadedIcon} />
);
};
LoadingIndicator.propTypes = {
classes: PropTypes.object,
className: PropTypes.string,
width: PropTypes.string,
};
export default LoadingIndicator;
You'll also need to put this button in a custom AppBar, inject your AppBar in a custom Layout, and use that Layout in your Admin, as explained in the react-admin Theming documentation.

I was able to turn off auto-refresh by dispatching the setAutomaticRefresh action from react-admin:
import { setAutomaticRefresh } from 'react-admin';
import { useDispatch } from 'react-redux';
// inside component
const dispatch = useDispatch();
dispatch(setAutomaticRefresh(false))
This action was added here as part of release 3.8.2.

Related

useSelector not responding to state changes in another Route -

I'm new to using Redux, and I'm working on a test project to better understand how to use it in conjunction with React Router. My goal is to have the state updated with user input onClick in the route edi1 and to then use useSelector in route edi2 to display the updated state. Currently, in route edi1, I can see the state updating both through useSelector and the Redux Profiler in the Dev. Tools. However, in route edi2 it renders the initial state from the store but does not respond to any state changes. I have been getting these results by clicking Submit in edi1 to update the state and then in a separate tab viewing route edi2. Even if I refresh the tab with 'edi2' the state remains as the initial state.
I've been looking through both React Router and Redux Toolkit Docs. In the React Router Docs, they recommend a "Deep Redux Integration" if you require for "Synchronize the routing data with, and accessed from, the store." (https://v5.reactrouter.com/web/guides/deep-redux-integration). I'm not sure if that would apply to my case here, but even if it did in the past, the docs are for version 5, and I'm using the updated version of React Router 6.4.1.
I've been stuck on this issue for days, so I appreciate any guidance to help me move forward from here. Please see the CodeSandBox below to review the code. I have also included a screenshot of the Profiler from Dev tools. If further information is needed, I will be happy to provide it. Thank you for your time!
Code:
https://codesandbox.io/s/render-redux-state-react-router-y46wqk?file=/src/App.js
Similar Issue :
Redux Toolkit useSelector not updating state in React component
The solution for their issue was to update React-Redux. I have version 8.0.4 and I think that is the most updated version.
Profiler:
store.js:
import { configureStore } from '#reduxjs/toolkit'
import ediReducer from './ediSlice'
export const store = configureStore({
reducer: {
edi: ediReducer,
},
})
edislice.js:
import {createSlice} from '#reduxjs/toolkit';
const initialState = {
id:0, content: ["Hello World"],
}
export const ediSlice = createSlice({
name: 'edi',
initialState,
reducers: {
addContent: (state, action) => {
state.content.push(action.payload); // add new content to the array
// state.value = action.payload; // this will overwrite the array
state.id += 1 // this will increment the id
console.log(state.id)
},
}
})
export const {addContent} = ediSlice.actions;
export default ediSlice.reducer;
SendEdi.js:
import { Editor } from '#tinymce/tinymce-react';
import {useRef, useState} from "react";
import {useSelector, useDispatch} from "react-redux";
import {addContent} from "../components/ediSlice.js";
export default function SendEdi() {
const ediContent = useSelector((state) => state.edi.content);
const dispatch = useDispatch()
const editorRef = useRef();
const [entryArray, setEntryArray] = useState();
const log = () => {
if (editorRef.current) {
setEntryArray(editorRef.current.getContent());
dispatch(addContent(entryArray));
console.log("Editor Conent:", {ediContent})
}}
let entryEdi = <Editor
onInit={(evt, editor) => editorRef.current = editor}
apiKey='your-api-key'
disabled = {false}
inline = {false}
init={{
selector: "#entryEdi",
height: 500,
menubar: false,
placeholder: "Whats on your mind?",
plugins: [
'advlist', 'autolink', 'lists', 'link', 'image', 'charmap', 'preview',
'anchor', 'searchreplace', 'visualblocks', 'code', 'fullscreen',
'insertdatetime', 'media', 'table', 'code', 'help', 'wordcount'
],
toolbar: 'undo redo | blocks | ' +
'bold italic forecolor | alignleft aligncenter ' +
'alignright alignjustify | bullist numlist outdent indent | ' +
'removeformat | help',
content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:14px }'
}}
/>
return (
<div>
{ediContent}
{entryEdi}
<button onClick={log}>Submit</button>
</div>
)
}
RecEdi.js:
import {useSelector} from "react-redux";
export default function RecEdi() {
const ediContent = useSelector((state) => state.edi.content);
return (
<div>
{ediContent}
</div>
)
}
App.js:
import {
BrowserRouter as Router,
Routes,
Route
} from "react-router-dom";
import Edi1 from "./pages/edi1";
import Edi2 from "./pages/edi2";
function App() {
return (
<div className="App">
<Router>
<Routes>
<Route path="/Edi1" element={<Edi1 />} />
<Route path="/Edi2" element={<Edi2 />} />
</Routes>
</Router>
</div>
);
}
export default App;
edi1.js:
import SendEdi from "../components/SendEdi.js";
import { content } from "../components/ediSlice.js";
export default function Edi1() {
return (
<div>
<SendEdi />
<h1>edi1</h1>
</div>
);
}
edi2.js:
import RecEdi from "../components/RecEdi";
import { useSelector } from "react-redux";
export default function Edi2() {
// const edicontent = useSelector((state) => state.content.value)
return (
<div>
<RecEdi />
<h1>edi2</h1>
</div>
);
}
Index.js:
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { store } from "./components/store.js";
import { Provider } from "react-redux";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
You do not need any React Router <-> Redux integration at all - you are reading up on the wrong topic here.
You are missing one point though: Redux is an in-memory router within that one tab you have open. So if you close the tab and open it again, it will initialize from the "initial state" again. The same goes for refreshing the page, or navigating in a way that unloads the page and navigating back to the page.
Depending on what you do here, that last part might be interesting: my guess is that while you navigate from page A to page B to see your result, you are doing so in a way that unloads the whole page and reloads it again, resulting in a complete re-initialization of your Redux store.
That would mean that you are navigating wrong (not using the components or methods provided by react-router), and that's what I'd be looking for.
Of course, if everything works and you want state to stay there over a page reload, you can look into something like redux-persist, but for right now you should find that navigation bug first or otherwise non-redux things will break as well.

Material UI v4 component styles being lost when its used in a MUI v5 only codebase

I have an older external component library which is still in the process of being migrated from MUI v4 to v5.
One of these components which is still using v4 needs to be used in a brand new codebase, which is set up for v5.
The problem is, this external component completely loses all of its styles when I attempt to use it in the newer codebase.
I would like to avoid having to install and set up MUI v4 o this new codebase. Did I miss something in the setup to be able to use components that are still on MUI v4?
Here is a rough sketch of my setup:
Older component from an external library
import { Grid, Button, makeStyles } from '#material-ui/core'
const useStyles = makeStyles((theme) => ({
gridRoot: {
// a bunch of styles
},
buttonRoot: {
// more styles
}
}))
const SomeOldComponent = () => {
const classes = useStyles()
return (
<Grid classes={{ root: classes.gridRoot }}>
I am an old component from an external library. I still use MUI v4.
<Button classes={{ root: classes.buttonRoot }}>Some button</Button>
</Grid>
)
}
export default SomeOldComponent
New codebase
import React from "react"
import { ThemeProvider } from "#mui/material/styles"
import { StylesProvider, createGenerateClassName } from '#mui/styles'
import { createTheme } from "./myTheme"
const classNameOpts = {
productionPrefix: '',
disableGlobal: true,
seed: 'mySeed',
}
const generateClassName = createGenerateClassName(classNameOpts)
export const ApplicationWrapper = ({ children }) => (
<StylesProvider generateClassName={generateClassName}>
<ThemeProvider theme={createTheme()}>{children}</ThemeProvider>
</StylesProvider>
)
export default ApplicationWrapper
Usage of the older component in the new codebase
The issue I have here is that in SomeOldComponent, none of its styles set in useStyles are being applied.
import { Grid, Button } from '#mui/material'
import SomeOldComponent from 'external-library'
const MyComponent = () => {
return (
<Grid>
I am a new component in a new MUI v5 only codebase
<SomeOldComponent/>
<Button>Do something!</Button>
</Grid>
)
}
export default MyComponent

Get SVG icons dynamically in Next.js

I wanted to get SVG icons dynamically. I found a method to do this but it seems I made some mistakes. Where am I doing it wrong?
Icon.js
import React from "react";
import { ReactComponent as Bollards } from "./icons/bollards.svg";
import { ReactComponent as Earthquake } from "./icons/earthquake.svg";
import { ReactComponent as Fire } from "./icons/fire.svg";
import { ReactComponent as Healthy } from "./icons/heartbeat.svg";
import { ReactComponent as Home } from "./icons/home.svg";
import { ReactComponent as Planting } from "./icons/planting.svg";
import { ReactComponent as Business } from "./icons/suitcase.svg";
import { ReactComponent as Travel } from "./icons/airplane-around-earth.svg";
const iconTypes = {
bollards: Bollards,
earthQuake: Earthquake,
fire: Fire,
healthy: Healthy,
home: Home,
planting: Planting,
business: Business,
travel: Travel
};
const IconComponent = ({ name, ...props }) => {
let Icon = iconTypes[name];
return <Icon {...props} />;
};
export default IconComponent;
Feautures.js
import React from "react";
import Icon from "./icon";
export default function Features() {
return (
<div>
<Icon name="bollards" />
</div>
);
}
I get this error when trying to export icons.
error - ./components/icon.js
Attempted import error: 'ReactComponent' is not exported from './icons/bollards.svg' (imported as 'Bollards').
You can use SVGR that allows us to import SVGs into your React applications as components.
You need to add #svgr/webpack as a dependency and modify the next.config.js file like this.
next.config.js:
module.exports = {
webpack(config) {
config.module.rules.push({
test: /\.svg$/,
use: ["#svgr/webpack"]
});
return config;
}
};
Then, you can simply import your icons without using ReactComponent.
Icon.js:
import React from "react";
import Bollards from './icons/bollards.svg';
import Earthquake from './icons/earthquake.svg';
import Fire from './icons/fire.svg';
import Healthy from './icons/heartbeat.svg';
import Home from './icons/home.svg';
import Planting from './icons/planting.svg';
import Business from './icons/suitcase.svg';
import Travel from './icons/airplane-around-earth.svg';
const iconTypes = {
bollards: Bollards,
earthQuake: Earthquake,
fire: Fire,
healthy: Healthy,
home: Home,
planting: Planting,
business: Business,
travel: Travel
};
const IconComponent = ({ name, ...props }) => {
let Icon = iconTypes[name];
return <Icon {...props} />;
};
export default IconComponent;
Working demo is available on CodeSandbox.

Export function from React functional component and use it elsewhere

I want to export function from one of my functional component that is using hooks to another one. I want to prevent redundant code appearing in my components.
I have tried to create separate function.js file where I wanted to place some of my functions but useDispatch hook makes it impossible as it throws hell a lot of errors in every attempt to make it work.
I was searching for solution and trying some export statements in different combinations.
What I want to do is to export my toggleDrawer function from Layout component to other components and here's my code. I'm sure it's very easy and I'm missing something.
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import Header from '../Header/header'
import DrawerItems from '../DrawerItems/drawerItems'
import { REDUCERS } from '../../Config/config'
import Container from '#material-ui/core/Container'
import Drawer from '#material-ui/core/Drawer'
import { makeStyles } from '#material-ui/core/styles'
const useDrawerStyles = makeStyles({
paper: {
width: '175px',
padding: '10px'
}
})
const Layout = props => {
const { isDrawerOpened } = useSelector(state => {
return {
...state.interface_reducer
}
})
const dispatch = useDispatch()
const drawerClasses = useDrawerStyles()
const toggleDrawer = (side, open) => event => {
if (event.type === 'keydown' && (event.key === 'Tab' || event.key === 'Shift')) {
return null
}
dispatch({
type: REDUCERS.TOGGLE_DRAWER,
payload: open
})
}
return (
<Container>
<React.Fragment>
<Header/>
<Drawer classes={{paper: drawerClasses.paper}} open={isDrawerOpened} onClose={toggleDrawer('left', false)} >
<DrawerItems/>
</Drawer>
{ props.children }
</React.Fragment>
</Container>
)
}
export default Layout
Define the function in another file. Or define it in that file and export it. Then you can import it in other files for other components.

Rendering Component with custom styles with Material UI V1

I am doing something fundamentally wrong leveraging Redux and Material UI V1, and I am looking for help. I have not been able to successfully style the width of my Card component at 75 pixels.
How are we suppose to pass custom styles to our components leveraging withStyles and connect? I have been following an example here in Material UI's docs on the Paper component but have not been able to style my UI successfully. Below are code snippets of my presentational and container component:
Container:
import compose from 'recompose/compose';
import { connect } from 'react-redux';
import { withStyles } from 'material-ui/styles';
import Home from '../components/Home';
import * as homeActions from '../modules/home';
const styles = theme => ({
root: theme.mixins.gutters({
width: 75,
}),
});
const mapStateToProps = (state, ownProps = {}) => {
return {
props: {
classes: styles,
welcomeMessage: state.home.message || ''
}
};
};
const mapDispatchToProps = (dispatch, ownProps = {}) => {
dispatch(homeActions.loadPage());
return {
};
};
export default compose(
withStyles(styles),
connect(mapStateToProps, mapDispatchToProps)
)(Home)
Presentational:
import Card, { CardActions, CardContent, CardMedia } from 'material-ui/Card';
import Button from 'material-ui/Button';
import PropTypes from 'prop-types';
import React from 'react';
import ReactDOM from 'react-dom';
import Typography from 'material-ui/Typography';
import photo from './party.png';
const Component = ({props}) => {
return (
<div>
<Card classes={{
root: props.classes.card
}}>
This is Paper
</Card>
</div>
);
}
Component.propTypes = {
props: PropTypes.object.isRequired
};
export default Component;
EDIT:
I have also leveraged this SO post to see how the Redux and Material UI API for styling have been combined.
Also, I did not explicit state this but according to Material UI, all Paper props are valid for Card props in case you were wondering why I cited Paper documentation.
I also replaced code snippets where previously there were directly links to my project.
I think it is acceptable for your presentational component to express its own style.
Export your presentational component using withStyles:
import Card from 'material-ui/Card';
import { withStyles } from 'material-ui/styles';
import PropTypes from 'prop-types';
import React from 'react';
const styles = theme => ({
root: theme.mixins.gutters({
width: 75,
}),
});
const Component = ({ classes }) => {
return (
<div>
<Card className={classes.root}>This is Paper</Card>
</div>
);
};
Component.propTypes = {
classes: PropTypes.shape({
root: PropTypes.string,
}).isRequired,
};
export default withStyles(styles)(Component);
Then, in your container, you simply connect the presentational component's default export:
export default connect(mapStateToProps, mapDispatchToProps)(Home)

Categories

Resources