I have integrated ReactCSSTransitionGroup, but am not getting an animation effect. Would appreciate tips on where the following structure is wrong:
import { Router, Route, IndexRoute, browserHistory } from 'react-router'
import ReactCSSTransitionGroup = require('react-addons-css-transition-group')
...
return (
...
<ReactCSSTransitionGroup
transitionName = "mainpage"
transitionEnterTimeout={ 500 }
transitionLeaveTimeout={ 300 }>
<Router key="router" history={ browserHistory }>
<Route key="/" path="/" component={ App }>
<IndexRoute key="MainTiles" component={ MainTiles } />
<Route key="Page1" path="page1" component={ Page1 } />
</Route>
</Router>
</ReactCSSTransitionGroup>
...
)
and my css is:
.mainpage-enter { opacity: 0; transition: opacity 100ms ease-in; }
.mainpage-enter.mainpage-enter-active { opacity: 1; transition: opacity 100ms ease-in; }
.mainpage-leave { opacity: 1; transition: opacity 100ms ease-in; }
.mainpage-leave.mainpage-leave-active { opacity: 0; transition: opacity 100ms ease-in; }
I believe the Components you want to have the transition lifecycle methods need to be the direct children of ReactCSSTransitionGroup.
For example:
<Router key="router" history={ browserHistory }>
<Route key="/" path="/" component={ App }>
<IndexRoute key="MainTiles" component={ MainTiles } />
<Route key="Page1" path="page1" component={ Page1 } />
</Route>
</Router>
Inside App.js:
class App extends Component {
render() {
return (
<div>
<ReactCSSTransitionGroup
component="div"
transitionName="example"
transitionEnterTimeout={500}
transitionLeaveTimeout={500}
>
{React.cloneElement(this.props.children, {
key: this.props.location.pathname,
})}
</ReactCSSTransitionGroup>
</div>
);
}
}
never mind, I found some sample code that works:
class App extends Component<any, any> {
render() {
return (
<div>
<ReactCSSTransitionGroup
component="div"
transitionName="mainpage"
transitionEnterTimeout={500}
transitionLeaveTimeout={500}
>
{
React.cloneElement(this.props.children, {
key: this.props.location.pathname
})
}
</ReactCSSTransitionGroup>
</div>
)
}
}
Apparently the main requirement is direct parentage, together with the injection of a key for the animated components.
Related
I want to set up authentication in my app, so I'm using firebase (I'm new to Firebase and authentication in react native in general).
When I open the app, for some reason it is already logged in, and displays AppStack, instead of AuthStack which is supposed to be the LoginScreen. When I run this code inside my AppStack on a component:
onPress={() => {firebase.auth().signOut().then(function() {
console.log('Signed Out');
}, function(error) {
console.error('Sign Out Error', error);
});}}>
it successfully logs 'Signed Out', but it doesn't switch screens.
I know the problem lies in my state variable, isLoggedIn, but I don't know how to fix it.
So this is my app.js, would really appreciate any feedback on how to make this work.
import * as React from "react";
...
import * as firebase from 'firebase';
var firebaseConfig = {
...
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();
function AuthStack() {
return(
<Stack.Navigator>
<Stack.Screen name="Login" component={LoginScreen} options={{ headerShown: false }} />
</Stack.Navigator>)}
function AppStack() {
return (
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
if (route.name === 'Order') {
return (
<IconWithBadge
name={focused ? 'ios-book' : 'ios-book'}
size={size}
color={color}
/>
);
} else if (route.name === 'Map') {
return (
<Ionicons
name={focused ? 'md-globe' : 'md-globe'}
size={size}
color={color}
/>
);
} else if (route.name === 'Profile') {
return (
<Ionicons
name={focused ? 'md-contact' : 'md-contact'}
size={size}
color={color}
/>
)
} else if (route.name === 'Notifications') {
return (
<Ionicons
name={focused ? 'ios-notifications-outline' : 'ios-notifications-outline'}
size={size}
color={color}
/>
)
}
},
})}
tabBarOptions={{
activeTintColor: 'orange',
inactiveTintColor: 'gray',
}}>
<Tab.Screen name="Order" component={OrderScreen} />
<Tab.Screen name="Map" component={MapScreen} />
<Tab.Screen name="Profile" component={ProfileScreen} />
<Tab.Screen name="Notifications" component={Notifications} />
</Tab.Navigator>)}
export default class App extends React.Component {
state = {
isLoggedIn: firebase.auth().onAuthStateChanged(user => { return user}),
}
render() {
return(
<NavigationContainer>
{ this.state.isLoggedIn ? (<AppStack />) : (<AuthStack />) }
</NavigationContainer>
)}
}
function Notifications() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Notifications</Text>
</View>
);
}
One thing you can do is :
export default class App extends React.Component {
state = {
isLoggedIn: false,
}
componentDidMount(){
firebase.auth().onAuthStateChanged(user => {
if(user) {
this.setState({ isLoggedIn: true })
} else {
this.setState({ isLoggedIn: false })
}
})
}
render() {
return(
<NavigationContainer>
{ this.state.isLoggedIn ? (<AppStack />) : (<AuthStack />) }
</NavigationContainer>
)}
}
I cannot open up the ResourcePicker for the life of me. I can see the state being changed from false to true when pressing the button, but the resourcepicker doesn't actually open.
<AppProvider apiKey={apiKey} shopOrigin={shopOrigin} >
<Page>
<TopBar pageTitle="Create Sale" primaryButton="Save" clickPrimaryButton={this.handleClick} />
<Layout sectioned={true}>
<Layout.AnnotatedSection title="Scheduled sale settings" description="Setup the discount and which products will be selected for the sale.">
<Card sectioned>
<FormLayout>
<TextField label="Sale name" onChange={() => { }} />
</FormLayout>
</Card>
<Card sectioned>
<FormLayout>
<DiscountValue />
</FormLayout>
</Card>
<Card>
<Card.Section>
<FormLayout>
<SaleCategory onSelect={this.handleSelect} />
</FormLayout>
</Card.Section>
{searchBar}
<Card.Section>
<Picker />
</Card.Section>
</Card>
</Layout.AnnotatedSection>
</Layout>
</Page>
</AppProvider>
and then the Picker Component
import React from 'react';
import { ResourcePicker, Button } from '#shopify/polaris';
class Picker extends React.Component {
constructor(props) {
super(props);
this.state = {
resourcePickerOpen: false,
}
}
render() {
return (
<div>
<pre>{JSON.stringify(this.state.resourcePickerOpen)}</pre>
<ResourcePicker
resourceType="Product"
open={this.state.resourcePickerOpen}
onSelection={({ selection }) => {
console.log('Selected products: ', selection);
this.setState({ resourcePickerOpen: false });
}}
onCancel={() => this.setState({ resourcePickerOpen: false })}
></ResourcePicker>
<Button onClick={() => this.setState({ resourcePickerOpen: !this.state.resourcePickerOpen })}>Open...</Button>
</div>
);
}
}
export default Picker;
I expect the picker to open, but it does not. What am I doing wrong?
Try to put your state declaration like the following code snippet
class Picker extends React.Component {
state = {
resourcePickerOpen: false,
}
...
}
you can delete your constructor
I have the following structure:
class Parent extends Component {
state = { isHeaderCollapsed : false }
render() {
<ScrollView decelerationRate="fast" stickyHeaderIndices={isHeaderCollapsed && [0]} scrollEventThrottle={1} onScroll={(value) => this.setState({isHeaderCollpased: true})} style={styles.body}>
<JobHeader collapsed={isHeaderCollapsed} />
<WebView allowsInlineMediaPlayback={true} style={[styles.video, style]} javaScriptEnabled={true} source={{uri: mediaUrl}} />
</ScrollView>
}
On iOS , very time I change the state of the parent component, the youtube video in the webview refreshes itself. I don't want this to happen.
Thanks !
You can use shouldComponentUpdate to check if you need rerender or not:
class Parent extends Component {
constructor(){
super();
this.state = {
isHeaderCollapsed : false
}
}
shouldComponentUpdate(nextProps, nextState){
return nextState.isHeaderCollapsed !== this.state.isHeaderCollapsed;
}
render() {
<ScrollView decelerationRate="fast" stickyHeaderIndices={isHeaderCollapsed && [0]} scrollEventThrottle={1} onScroll={(value) => this.setState({isHeaderCollpased: true})} style={styles.body}>
<JobHeader collapsed={isHeaderCollapsed} />
<WebView allowsInlineMediaPlayback={true} style={[styles.video, style]} javaScriptEnabled={true} source={{uri: mediaUrl}} />
</ScrollView>
}
I have followed the example here to try and create a basic authenticated area
for my app, which is a solution I really like in principle. Here is my index.js:
const store = createStore(reducer);
ReactDOM.render(
<Provider store={store}>
<Router history={hashHistory}>
<Route path="/" component={App}>
<IndexRedirect to="authenticated" />
<Route path="setup" component={SetupJourney} >
<Route path="details" component={Details}/>
<Route path="account" component={AccountType}/>
</Route>
<Route path="authenticated" component={requireAuthentication(secretPage)} />
</Route>
</Router>
</Provider>,
document.getElementById('root')
);
and then here's my AuthenticatedComponent higher-order component to handle redirects:
export function requireAuthentication(Component) {
class AuthenticatedComponent extends React.Component {
componentWillMount() {
this.checkAuth();
}
componentWillReceiveProps(nextProps) {
this.checkAuth();
}
checkAuth() {
if (!this.props.isAuthenticated) {
this.props.dispatch(pushState(null, '/setup/details', ''));
}
}
render() {
return (
<div>
{this.props.isAuthenticated === true
? <Component {...this.props}/>
: null
}
</div>
)
}
}
const mapStateToProps = (state) => ({
isAuthenticated: state.get('user').get('isAuthenticated')
});
return connect(mapStateToProps)(AuthenticatedComponent);
}
I've been playing about with this for ages and I cannot get the component to redirect. In Redux dev tools I can see the ##reduxReactRouter/historyAPI action has fired, but the URL doesn't change. All the relevant props/state etc seem to be in place too...is there something I've missed?
Thanks
For anyone coming across this, I eventually resolved this and there were a few problems. Firstly, pushState is only for use with browserHistory, so I needed to switch from hashHistory.
After this the pushState still did not work. This can apparently happen when middleware is specified in the wrong order. I restructured my index.js to follow the pattern in Redux's real world example, and everything eventually worked.
The key bit is I now have a store.js file that looks like this:
import { createStore, applyMiddleware, combineReducers, compose } from 'redux';
import { routerReducer, routerMiddleware} from 'react-router-redux';
import { browserHistory } from 'react-router';
import reducer, { INITIAL_STATE } from '../reducers/reducer';
const rootReducer = combineReducers({reducer, routing: routerReducer});
const composeEnhancers =
process.env.NODE_ENV !== 'production' &&
typeof window === 'object' &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
}) : compose;
const enhancer = composeEnhancers(
applyMiddleware(routerMiddleware(browserHistory))
);
const initialState = {'reducer': INITIAL_STATE};
export default function configureStore() {
return createStore(rootReducer, initialState, enhancer);
}
I'm trying to use React Router in my react app which is bounded as wordpress plugin and uses flux to fetch api data.
my entry point looks as it follows
import React from 'react';
import Workshops from './components/workshops';
import Workshop from './components/workshop';
import NotFound from './components/notfound';
import Router, { Route, DefaultRoute, NotFoundRoute, Redirect, Link } from 'react-router';
import json from './config.json';
localStorage.clear();
localStorage.setItem('workshops', JSON.stringify(json));
const AppRoutes = (
<Route path="/" handler={Workshops}>
<DefaultRoute handler={Workshop} />
<Route name="workshop" path=":slug" handler={Workshop}/>
<NotFoundRoute handler={NotFound} />
</Route>
);
Router.run(AppRoutes, Router.HashLocation, (Root) => {
React.render(<Root />, document.getElementById('workshop-booker'));
});
than in my Workshops component I make some links to a given route, I have hash changes but the routed component does not getting fired.
<h3> <Link to="workshop" params={{slug: workshop.slug }}> {workshop.title.rendered }</Link></h3>
You can wrap your Router with a DebugRouter which will print the navigation actions made:
import React, { Component } from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import Login from 'components/Login'
import DefaultComponent from 'components/DefaultComponent'
class DebugRouter extends BrowserRouter {
constructor(props){
super(props);
console.log('initial history is: ', JSON.stringify(this.history, null,2))
this.history.listen((location, action)=>{
console.log(
`The current URL is ${location.pathname}${location.search}${location.hash}`
)
console.log(`The last navigation action was ${action}`, JSON.stringify(this.history, null,2));
});
}
}
class App extends Component {
render() {
return (
<DebugRouter>
<Switch>
<Route exact path="/login" name="Login Page" component={Login} />
<Route path="/" name="Home" component={DefaultComponent} />
</Switch>
</DebugRouter>
);
}
}
link to the gist
I made my DebugRouter for functional components
const DebugRouter = ({ children }: { children: any }) => {
const { location } = useHistory()
if (process.env.NODE_ENV === 'development') {
console.log(
`Route: ${location.pathname}${location.search}, State: ${JSON.stringify(location.state)}`,
)
}
return children
}
const Router = () => {
return (
<BrowserRouter>
<Layout>
<Route
render={() => {
return (
<DebugRouter>
<Switch>
<Redirect exact from="/" to={...} />
// <Route/> should be here
<Redirect from="*" to={...} />
</Switch>
</DebugRouter>
)
}}
/>
</Layout>
</BrowserRouter>
)
}
You can try something like this
import React, {Component} from 'react';
import PropTypes from 'prop-types'
import {withRouter} from "react-router-dom";
class RouterDebugger extends Component {
componentWillUpdate(nextProps, nextState){
console.log('componentWillUpdate',nextProps, nextState)
}
componentDidUpdate(prevProps) {
console.log('componentDidUpdate',prevProps)
}
render() {
return null
}
}
export default withRouter(RouterDebugger)
And insert this component in any place you want to debug.
You can pass a prop with some identifier
i hope this help you
You can use the following code to debug React Router:
console.log(this.props.location)
console.log(this.props.match)