In my react app, I'm trying to achieve the results of toggling background colors of a graph, even if a graph is already loaded/rendered on screen.
With my current code, if I click the checkbox to toggle the colors it works perfectly fine as far as setting the colors in state and passing those into my component, but I have to reload the component on screen to see the changes, but they do show at that point.
I've tried to use forceUpdate() to rectify this but to no avail
What should I do at this point to make sure that when the state changes in this function it reloads the component completely?
state = {
renderCalories: true,
graphBackgroundColor: '#fff',
graphLabelColor: '#000'
};
toggleCOlor = event => {
console.log('toggling color');
this.setState({
graphBackgroundColor: '#000',
graphLabelColor: '#fff'
});
this.forceUpdate();
}
render() {
return (
<div>
<div className={styles.dropDown}>
<FormControl style={{minWidth: 120}} className={styles.formControl}>
<FormControlLabel
value="end"
control={<Checkbox color="primary" />}
label="Toggle Graph Background Color"
labelPlacement="end"
onChange={this.toggleCOlor}
/>
</FormControl>
</div>
{this.state.renderCalories && <NetCalorieTrend backgroundColor={this.state.graphBackgroundColor} labelColor={this.state.graphLabelColor} />}
...
}
Related
guys I have a question about navigation in react native.
So I mainly use TabNavigator. I have 2 main stack navigators in the app
const Tab = createBottomTabNavigator();
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeStackScreen} />
<Tab.Screen name="Profile" component={ProfileStackScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
In my ProfileStack screen, I have also two pages: MyProfile and UsersProfile:
const ProfileStack = createNativeStackNavigator();
function ProfileStackScreen({route, navigation}) {
return (
<ProfileStack.Navigator initialRouteName="MyProfile">
<ProfileStack.Screen name="MyProfile" component={MyProfilePage} />
<ProfileStack.Screen name="UserProfile" component={UserProfilePage} options={{
headerLeft: () => (<View>
<Button title="back" onPress={() => {navigation.goBack()}}/>
</View>)
}}/>
</ProfileStack.Navigator>
);
}
Now I want to navigate from the HomeScreen to the UserProfilePage and pass params to this screen. I'm doing it like this:
function HomeScreen({ navigation }) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Home screen this</Text>
<Button
title="Go to another user profile"
onPress={() => navigation.navigate('Profile', {screen: 'UserProfile', params: {userId: 1235}})}
/>
</View>
);
}
So now, when I come to the page UserProfile I see that it loads also the Profile page, and just for a second I see blinking of ProfilePage and its UI that is not cool, and should it be like so? I guess not.
Then if I press the BACK button on UsersProfilePage, I'm navigating back to HomeScreen - and this is ok! This is what I expect!
But now, If I will press ProfileTab I see only UsersProfilePage but not MyProfilePage. When I press the BACK button again, I go back to the HomeScreen that is weird for me. Can u explain why it happens? Why I don't get back to the MyProfilePage.
I prepared an expo snack here. You can reproduce this behavior.
This is because you’re navigating to a screen in a nested navigator. It is ignoring the initial route. Then when you press the tab again it is still mounted and will still have the previous route state, which is just the screen without the initial screen.
By default, when you navigate a screen in the nested navigator, the specified screen is used as the initial screen and the initial route prop on the navigator is ignored. This behaviour is different from the React Navigation 4.
If you need to render the initial route specified in the navigator, you can disable the behaviour of using the specified screen as the initial screen by setting initial: false:
navigation.navigate('Root', {
screen: 'Settings',
initial: false,
});
See https://reactnavigation.org/docs/nesting-navigators/#rendering-initial-route-defined-in-the-navigator and https://reactnavigation.org/docs/navigation-lifecycle/
I'm currently working on an admin portal using react-admin, and I've been able to seamlessly style it using MaterialUI, specifically its makeStyles hook. The way I've done it is I'd import my makeStyles file that looks similar to this:
export const detailsView = makeStyles({
detail: {
width: "100%",
"& div": {
display: "Block",
visibility: "Visible",
}
}
export default detailsView
into my main component, that would hold, for example, a List component like this:
export const ExampleList = (props) => {
const classes = importedStyles();
return (
<>
<h2 className={classes.tableTitle}>Example Header</h2>
<List {...props} className={classNames(classes.table)}>
<Datagrid rowClick="edit">
<ReferenceField source="id" reference="users" label="Name">
<FunctionField
label="Name"
render={(record) => `${record.first_name} ${record.last_name}`}
/>
</ReferenceField>
<TextField source="title"/>
<DateField source="date" />
</Datagrid>
</List>
</>
);
};
export default ExampleList;
If I had to wrap my own div that would also hold a section of a TextField, everything still works according to plan. However, this is only the case with the List, Edit, Filter, and Create, however, I'm unable to properly style the Show component.
Prior to adding any styles or wrapping some fields up in a div, this is how it would look like: Notice how the name shows up just fine.
However, when I wrap the Person Details header with the rendered name, as seen below:
<div className={detailClasses.detailSection}>
<h3 className={ detailClasses.detailSectionTitle}>Person Details</h3>
<ReferenceField
source="id"
reference="users"
label="Name"
className={detailClasses.detailSectionInfo}
>
<FunctionField
render={(record) => `${record.first_name} ${record.last_name}`}
/>
</ReferenceField>
</div>
then the information that I want rendered (in this case, the person's name), then it completely stops rendering and doesn't show up on the page.
This hasn't happened with any other react-admin component that I mentioned somewhere above.
I was wondering why this is the case, and why it is only happening with the Show component.
Thanks in advance!
I'm setting up a generic loading strategy for NextJS apps using framer-motion.
function MyApp({ Component, pageProps, router }) {
const [isFirstMount, setIsFirstMount] = useState(true);
useEffect(() => {
const handleRouteChange = () => {
setIsFirstMount(false);
};
router.events.on("routeChangeStart", handleRouteChange);
// If the component is unmounted, unsubscribe
// from the event with the `off` method:
return () => {
router.events.off("routeChangeStart", handleRouteChange);
};
}, [router]);
console.log("My App ran");
return (
<Layout>
<AnimatePresence exitBeforeEnter>
<motion.div exit={{ opacity: 0 }}>
{isFirstMount && <InitialTransition />}
<motion.div
initial="initial"
animate="animate"
variants={content(isFirstMount)}
className="space-y-12"
>
<Component
isFirstMount={isFirstMount}
key={router.route}
{...pageProps}
/>
</motion.div>
</motion.div>
</AnimatePresence>
</Layout>
);
}
I ran into an issue with params. When using params during initial loading (such as clicking on the refresh button on browser), NextJS loads an additional time. I believe its due to NextJS's router query object update. For example,
WEBSITE.com/ // works - you will see the black loading screen, the black loading screens slides down, and the child objects start popping on
// vs
WEBSITE.com/?test=test // fails - you will see the black loading screen flash in and out of view before the rest of the app proceeds
I made a codesandbox sample. On the browser side, test by adding or removing ?test=test to the end of the url.
How can I prevent the additional re-render or get the loader to work the same way regardless of having a param or not?
Update
I tried #Mokhtar 's code. Technically, it did nothing yet the transitioning started worked. Eventually I figured out that removing all the isFirstMount and useEffect logic would make the transitioning correctly.
function MyApp({ Component, pageProps, router }) {
console.log("My App ran");
return (
<Layout>
<AnimatePresence exitBeforeEnter>
<motion.div exit={{ opacity: 0 }}>
<InitialTransition />
<motion.div
initial="initial"
animate="animate"
variants={content(true)}
>
<Component key={router.route} {...pageProps} />
</motion.div>
</motion.div>
</AnimatePresence>
</Layout>
);
}
Does anyone know why its working? I thought the exit opacity / sliding action only works when the Component is removed.
Your <InitialTransition /> component is getting unmounted because of your route being called with a shallow change (https://nextjs.org/docs/routing/shallow-routing) and therefore calling your handleRouteChange handler.
The following handleRouteChange implementation should fix it.
const handleRouteChange = (url, { shallow }) => {
if (shallow && !isFirstMount) {
setIsFirstMount(false);
}
};
I am here facing an interesting issue not sure why it is happening.
Basically i have set that for any on click event will trigger the toggleDropDown() which will invert the condition isDropdownOpen (to true or false) and that will help in showing the dropdown menu {isDropdownOpen ? <DropDownlist /> : false}.
I am seeing after 1st click the condition for isDropdownOpen changes to true and thats fine and i click again it changest o false thats fine too but when i do the third click and any later click it remains false, and i can see the elelel remain on the nav bar, any suggestion where i am doing wrong. Thanks
After First click on usericon in navbar ,user-DropdownOpenis set to true
After Second and any later clicks,user-DropdownOpen`remains false
Updated code below &small clip of current status current behaviour of the application
Snippet of Nav.js Component code
import history from "./History";
function Nav({ userinfo, userstatus }) {
const [isDropdownOpen, setDropdownOpen] = useState(false);
const toggleDropDown = (e) => {
e.preventDefault();
history.push("/signin");
return (
<nav className="header">
<label className="logo">
<a href="/">
<img className="yoga-image" src={ProjectLogo} />
</a>
</label>
<ul>
<li>
<a
className="glyphicon glyphicon-shopping-cart
"
href="./basket"
></a>
</li>
<li>
<a className="active" href="./signin">
{userstatus ? (
<button
style={{ border: "none", background: "none", outline: "none" }}
className=" signin-icon glyphicon glyphicon-user
"
onClick={(e) => toggleDropDown(e)}
></button>
) : (
<button style={{ border: "none", background: "none", outline: "none" }} onClick={(e) => toggleDropDown(e)}>
SIGNIN
</button>
)}
</a>
</li>
</ul>
{isDropdownOpen && <DropDownlist />}
</nav>
);
}
Snippet of History.js Component code
import { createBrowserHistory } from "history";
export default createBrowserHistory();
Snippet of App.js Component code
```
import Signin from "./Component/Signin";
import history from "./History";
return (
<Router history={history}>
<div className="App">
<header className="header">
<Nav userinfo={userData} userstatus={siginalready} />
</header>..................
<Switch> // here we got switch statement contains(/,/basket,/signin)
<Route
path="/"
exact
render={(props) => <Home {...props} userData={userData} userstatus={siginalready} addBasketitems={addBasketitems} />}
/>
<Route
path="/basket"
exact
render={(props) => (
<Basket {...props} userData={userData} userstatus={siginalready} basketItems={basketItems} updatedBasket={updatedBasket} resetBasket={resetBasket} />
)}
/>
<Route
path="/signin"
exact
render={(props) => <Signin {...props} buyNow={buyNow} resetBuynow={resetBuynow} userData={userData} finalBuy={finalBuy} userstatus={siginalready} />}
/>
<div className="footer">
<Footer />
</div>
</div>
</Router>
);
Snippet of Signin.js Component code
function Signin({ userData, userstatus, finalBuy, buyNow, resetBuynow }) {
//POST request to server...................and recieve Invoices
return <div>{userstatus ? <Useraccount userinfo={userData} userstatus={userstatus} finalBuy={finalBuy} allInvoices={allInvoices} /> : <SigninOptions />}</div>;
}
export default Signin;
Try setting initial state to false instead of getting it from the localStorage, so that its closed in the beginning (I suspect that this might be your issue, since when you set the state its already in the local storage and you will never get a false again as an initial condition, unless the storage is set to false):
const [isDropdownOpen, setDropdownOpen] = useState(false);
Then when you are setting the state in the toggle function do:
setDropDownOpen(prevState => return { !prevState });
When you change a state of a component, you have to use the previous state to do that (might not be the actual issue that you are having, its just a good practice).
Then at the end of your function where you are rendering the DropDownList change it to:
{isDropDownOpen && <DropDownList />}
This is something like a "binary operator" so it will render DropDownList only if isDropDownOpen is true, otherwise it will not do anything.
I am suspecting that the issue might have to do something with the function that you made initialDropDownOpen(), see if the above changes fix it.
P.S. You can implement the changes one by one and see which if any of them fixes it :) and please let me know! :)
EDIT: Lift the state up, move
const [isDropdownOpen, setDropdownOpen] = useState(false);
into the parent component and then add a property to your dropDown, for example call it show, so:
<Nav show={isDropDownOpen} toggle={setDropdownOpenWrapper} ...otherprops.../>
You have to wrap the set function and pass the wrapped function:
const setDropdownOpenWrapper = () => { setDropDown(prevState => return { !prevState}) };
and then use show and toggle inside the Nav component.
It is happening due to tag have event attribute. When you click it, it tries to send form attached to it as default and redirect url or main domain. What you can do is simple. just using
e.preventDefault()
You need to update onClick and toggleDropDown functions like following
onClick;
{userstatus ? (
<button
style={{ border: "none", background: "none", outline: "none" }}
className=" signin-icon glyphicon glyphicon-user "
onClick={e => toggleDropDown(e)}
></button>
) : (
<button
style={{ border: "none", background: "none", outline: "none" }}
onClick={e => toggleDropDown(e)}
>
SIGNIN
</button>
)}
toggleDropDown;
const toggleDropDown = e => {
e.preventDefault();
setDropdownOpen(prevState => !prevState);
};
with these updates it will stay at your component or will not refresh the page or will not try to render your component again.
Also for localStorage you can use useEffect to update state.
define your state false/true as default, then write a useEffect hook.
useEffect(() => {
if (localStorage.getItem("test")) {
setDropdownOpen(localStorage.getItem("test"));
}
}, []);
I'm a newbie in React Native and struggling in cleaning up the state of the screen.
Like screen A has some states --> screen B, back to screen A, old states are clear. I'm using React Navigation V5
What I'm trying to do is:
After navigating from MainMap.js to the last screen TripsListScreen.js (the whole process is a Stack of 4 screens, nested in a Drawer), I got all the data stored in Redux's store, and display it in TripsListScreen.
The problem is when I press the add button in TripsListScreen to comeback at the MainMap.js, it doesn't clean up every state as I expect.
Here's the MainMap.js 's states:
const initialState = {
hasMapPermission: false,
_userLocationDisplayed: null,
userLatitude: 0,
userLongitude: 0,
initial_UserLatitude: 0,
initial_UserLongitude: 0,
userLocationAddress: '',
destination: [],
destinationName: [],
destinationAddress: [],
destinationCoords: [],
destinationImageUri: [],
numOfInput:[0,1],
counter: 1,
wayPoints: [],
markers: [],
}
class MainMap extends React.Component{
constructor(props){
super(props);
this.state = initialState;
};
componentDidMount(){
console.log('Hello',this.props)
if(this.props.route.params === true){
this.setState(initialState)
}
this._requestUserLocation();
};
Basically, I tried to pass a boolean param from TripsListScreen to MainMap, and if the param is true, I'll set all the states back to the beginning. However, it doesn't work as expected.
Here's TripsListScreen:
//...
<Layout style={styles.bottom}>
<TouchableOpacity onPress={() => props.navigation.navigate('PlanningProcess', {
screen: 'MainMapScreen',
params: {doAddPlace: true}
})} style={styles.createTrip}>
<Layout style={styles.button} >
<Icon name='add' size={35} />
</Layout>
</TouchableOpacity>
</Layout>
//...
Here's the Navigation:
StackNavigators:
const Stack = createStackNavigator();
const StackNavigator = (props) => {
return(
<Stack.Navigator screenOptions={{headerShown: false}}>
<Stack.Screen name='MainMapScreen' component={MainMap} />
<Stack.Screen name='TripDescription' component={TripDescription} />
<Stack.Screen name='TripsListDetailScreen' component={TripsListDetailScreen} />
<Stack.Screen
name='TripsListScreen'
component={TripsListScreen}
options={{
headerLeft: () => (<Icon style={{marginHorizontal: 30, marginTop: 30}} color='white' name='menu' size={30} onPress={() => props.navigation.dispatch(DrawerActions.openDrawer())}/>),
title:'Something'
}}
/>
</Stack.Navigator>
);
};
export default StackNavigator;
Main Navigators:
const Navigator = () => {
return(
<NavigationContainer>
<Drawer.Navigator
statusBarAnimation='slide'
drawerContent={props =>
<DrawerContent {...props} />}>
<Drawer.Screen name='Welcome' component={WelcomeStackScreen} />
<Drawer.Screen name='TripsListScreen' component={TripsListScreen} />
<Drawer.Screen name='PlanningProcess' component={StackNavigators} />
</Drawer.Navigator>
</NavigationContainer>
);
};
export default Navigator;
This is what MainMap renders:
This is what I expected, when navigating from TripsListScreen ( to create a new trip):
PLEASE HELP !!!
ComponentDidMount in MainMap.js doesn't get triggered because the screen is mounted already. Please look at this `componentDidMount()` function is not called after navigation
The method ComponentDidMount() only triggers for the first time at the mounting of the component and as you are navigating to a new screen, the previous screen is still mounted in the stack.
If you want to re-initialize your state every time your component gets the focus, you can set a listener on the focus on the navigation.
Like this,
const unsubscribe = navigation.addListener('willFocus', () => {
// Do something
// re-initialise the state
});
in StackNavigator, screens don't unmount when you open new screens on top of them. So if you go from A to B, then from B to C, both A and B will stay mounted. If you go back from C to B, C will unmount. It's like push and pop methods on array. componentDidMount in MainMap is not being called when you go back to it, as it doesn't unmount in first place. It is explained here Navigation Lifecycle.
As you are using Redux and saying that all the data is stored in Redux store, make your MainMap component render solely from the data from store, not from own state. You can then manipulate this data from TripsListScreen by dispatching actions. The easiest would be creating something like RESET_MAIN_MAP action that will reset that part of the state for MainMap screen