React.js scrollTop animation - animation

I basically want to animate scrollTop in React when a react property changes value. A "cut-down" Component I am using looks something like this:
import React, { Component } from 'react';
class ScrollComponent extends Component {
constructor(props) {
super(props);
this.scrollTo = this.props.scrollTo;
}
componentDidUpdate() {
//Animate "container" to scroll to this.props.scrollTo in 1000ms
}
render() {
return (
<div ref="container" style={{height:'300px', width:'100%', overflowY:'scroll',position:'relative'}}>
<div style={{height:'1000px'}}>
</div>
</div>
)
}
}
I guessing this can easily be done using jQuery, a bit trickier with native JavaScript. What is the correct way to do this with React?
I am aware of ReactCSSTransitionGroup and ReactTransitionGroup, if there is an easy way I can do it with these addons, that would be great, if there is another way, even better.
Thanks.

Related

TouchableHighlight onpress Android

I am using react native to develop a simple Touchablehighlight element with onpress event. iOS is ok to go but Android takes few seconds to load a function each time. I have looked up few possible solutions such as TouchableWithoutFeedback, TouchableNativeFeedback, etc.
My idea is to click a TouchableHighlight -> call handlePress function -> alert('hello world') under a flatlist
Here is my code.
import React from "react";
import { Image, TouchableHighlight, Platform, TouchableNativeFeedback, View, Text } from "react-native";
import styles from "./Styles";
// Plugin
import FastImage from "react-native-fast-image";
class BacktoTop extends React.Component {
constructor(props) {
super(props);
}
_handlePress = () => {
alert('hello world')
}
render() {
return (
<TouchableHighlight
style={ styles.container }
underlayColor={"#ffffff"}
// onPress={this.props.handlePress }
onPress={this._handlePress }>
<FastImage
source={
(Platform.OS === 'ios') ?
require("../../images/back_to_top.png")
:
{
// 6 months at least 1 view no deletion
uri: 'https://image.ibb.co/grvFS8/back_to_top.png',
priority: FastImage.priority.cacheOnly,
}
}
resizeMode={FastImage.resizeMode.cover}
style={{ width: 35, height: 35 }} />
</TouchableHighlight>
);
}
}
export default BacktoTop;
Thank you so much for your help.
I think the more view components in the upper level to be rendered, the longer time it takes to load.
After I deleted some of my in parent and removed onMomentumScrollEnd the reaction is improved.
Thanks.

undefined this.props.nnavigation.navigate('screen'), how to fix?

Im trying to use Drawer navigation. I have header.js components which has a hamburger icon to open the left navigation menu
//Header.js
import React from 'react';
import { Text, View, Image, TouchableHighlight} from 'react-native';
import { DrawerNavigator } from 'react-navigation';
// Make a component
const Header = (props, {navigate}) => {
const { textStyle, viewStyle, imgstyle } = styles;
return (
<View style={viewStyle}>
<TouchableHighlight onPress={()=> this.props.navigation.navigate('DrawerToggle')}> //here the error is coming
<Image style={{width:40, height:40}}
source={require('./../media/hamburger.png')}
/>
</TouchableHighlight>
<Text style={textStyle}>{props.headerText}</Text>
</View>
);
};
export default Header;
Here is App.js
const MyApp = DrawerNavigator({
Login: {
screen: Login,
},
Signup: {
screen: Signup,
},
});
export default class App extends React.Component {
render() {
return <MyApp />;
}
}
Here is one of the comoponent Login.js
import React, { Component } from 'react';
import {
Text, View, StyleSheet
} from 'react-native';
import Header from './Header';
export default class Login extends Component{
static navigationOptions = {
drawerLabel: 'Login',
};
render(){
return(
<View >
<Header headerText={'Login'}/>
<Text>Login Screen</Text>
</View>
);
}
}
I am not sure what I am missing to fix this. Please check my code.
EDITED:
I tried without "this" to but no luck. I am checking everywhere in the internet but couldn't find the exact solution or I couldn't get a solution to my mind. Can someone who is good with react native respond on this, Please?
The problem is - you use this keyword in a stateless component: this.props.navigation.navigate('DrawerToggle').
Try just props.navigation.navigate('DrawerToggle') since this is not pointing to the Header component instance in your case.
this in a stateless component is "inherited" from the calling scope (since stateless component is an "arrow" function) and likely points to window or global.

React components how to execute a button that calls a function from a different component

i've been trying to execute a button that calls a function from a different component.
import ComponentB from './components/ComponentB '
import React, {Component} from 'react';
class ComponentA extends Component {
render() {
return
(
<button onClick={this.handleClick}>click me</button>
);
}
}
export default ComponentA;
this didn't work out. the button wasnt able to call the function. what am i doing wrong?
import React, {
Component
}
from 'react';
class ComponentB extends Component {
constructor() {
this.handleClick = this.handleClick.bind(this);
}
}
handleClick() {
console.log("hi hi hi");
}
}
export
default ComponentB;
You're importing ComponentB, but not using it, that's why is not working. In your case the best way is to implement the handleClick directly to your ComponentA, but this is not what you wanna do. If you're looking to share the same functions with different components, using redux together with react js will get the job done and will provided to the other components as well ;)

React - delay heavy sub components rendering

I'm using React and React Router, and on some pages, I have some big lists rendered (as sub components) of the pages. When clicking on a link to change the page, it waits for all the subcomponents to be rendered which implies a delay between the click and the visual feedback. Is there a simple way not to wait for some sub components to be rendered?
I'm looking for a best practice, but maybe using a boolean and a setTimeout() is the only way. Thx.
When you have big lists of components, you often don't see all of them at the same time. You can use React Virtualized to actually render only visible components.
Check the code below:
const LightComponent = () => <div>LightComponent</div>
const HeavyComponent = () => <div>HeavyComponent</div>
class App extends React.Component {
constructor(props) {
super();
this.state = { shouldRenderHeavyComponent: false };
}
componentDidMount() {
this.setState({ shouldRenderHeavyComponent: true });
}
render() {
console.log("Render", this.state.shouldRenderHeavyComponent);
return (
<div>
<LightComponent/>
{ this.state.shouldRenderHeavyComponent && <HeavyComponent/> }
</div>
);
}
}
ReactDOM.render(<App/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
I had the same challenges. Finally I discovered this wonderful library, doing the trick for me: https://github.com/seatgeek/react-infinite

Animated page transitions in react

The past couple of weeks I've been working on an app using React. So far everything is working fine, but now I want to add some transitions to it. These transitions are a bit more complex than any examples I managed to find.
I've got 2 pages, an overview and a detail page which I'd like to transition between.
I'm using react-router to manage my routes:
<Route path='/' component={CoreLayout}>
<Route path=':pageSlug' component={Overview} />
<Route path=':pageSlug/:detailSlug' component={DetailView} />
</Route>
Overview looks like this:
Detailview looks like this:
The idea of the transition is that you click on one of the elements of the Overview. This element which has been clicked moves towards the position it should have on the detailView. The transition should be initiated by a route change (I think) and should also be able to happen in reverse.
I've already tried using ReactTransitionGroup on the Layout, which has a render method which looks like this:
render () {
return (
<div className='layout'>
<ReactTransitionGroup>
React.cloneElement(this.props.children, { key: this.props.location.pathname })
</ReactTransitionGroup>
</div>
)
}
This will give the child component the ability to receive the special lifecycle hooks. But I'd like to access the child components somehow during these hooks and still keep doing things the React way.
Could someone point me in the right direction for the next step to take? Or maybe point me to an example which I may have missed somewhere? In previous projects I used Ember together with liquid fire to get these kinds of transitions, is there maybe something like this for React?
I'm using react/react-redux/react-router/react-router-redux.
Edit: Added a working example
https://lab.award.is/react-shared-element-transition-example/
(Some issues in Safari for macOS for me)
The idea is to have the elements to be animated wrapped in a container that stores its positions when mounted. I created a simple React Component called SharedElement that does exactly this.
So step by step for your example (Overview view and Detailview):
The Overview view gets mounted. Each item (the squares) inside the Overview is wrapped in the SharedElement with a unique ID (for example item-0, item-1 etc). The SharedElement component stores the position for each item in a static Store variable (by the ID you gave them).
You navigate to the Detailview. The Detailview is wrapped into another SharedElement that has the same ID as the item you clicked on, so for example item-4.
Now this time, the SharedElement sees that an item with the same ID is already registered in its store. It will clone the new element, apply the old elements position to it (the one from the Detailview) and animates to the new position (I did it using GSAP). When the animation has completed, it overwrites the new position for the item in the store.
Using this technique, it's actually independent from React Router (no special lifecycle methods but componentDidMount) and it will even work when landing on the Overview page first and navigating to the Overview page.
I will share my implementation with you, but be aware that it has some known bugs. E.g. you have to deal with z-indeces and overflows yourself; and it doesn't handle unregistering element positions from the store yet. I'm pretty sure if someone can spend some time on this, you can make a great little plugin out of it.
The implementation:
index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import Overview from './Overview'
import DetailView from './DetailView'
import "./index.css";
import { Router, Route, IndexRoute, hashHistory } from 'react-router'
const routes = (
<Router history={hashHistory}>
<Route path="/" component={App}>
<IndexRoute component={Overview} />
<Route path="detail/:id" component={DetailView} />
</Route>
</Router>
)
ReactDOM.render(
routes,
document.getElementById('root')
);
App.js
import React, {Component} from "react"
import "./App.css"
export default class App extends Component {
render() {
return (
<div className="App">
{this.props.children}
</div>
)
}
}
Overview.js - Note the ID on the SharedElement
import React, { Component } from 'react'
import './Overview.css'
import items from './items' // Simple array containing objects like {title: '...'}
import { hashHistory } from 'react-router'
import SharedElement from './SharedElement'
export default class Overview extends Component {
showDetail = (e, id) => {
e.preventDefault()
hashHistory.push(`/detail/${id}`)
}
render() {
return (
<div className="Overview">
{items.map((item, index) => {
return (
<div className="ItemOuter" key={`outer-${index}`}>
<SharedElement id={`item-${index}`}>
<a
className="Item"
key={`overview-item`}
onClick={e => this.showDetail(e, index + 1)}
>
<div className="Item-image">
<img src={require(`./img/${index + 1}.jpg`)} alt=""/>
</div>
{item.title}
</a>
</SharedElement>
</div>
)
})}
</div>
)
}
}
DetailView.js - Note the ID on the SharedElement
import React, { Component } from 'react'
import './DetailItem.css'
import items from './items'
import { hashHistory } from 'react-router'
import SharedElement from './SharedElement'
export default class DetailView extends Component {
getItem = () => {
return items[this.props.params.id - 1]
}
showHome = e => {
e.preventDefault()
hashHistory.push(`/`)
}
render() {
const item = this.getItem()
return (
<div className="DetailItemOuter">
<SharedElement id={`item-${this.props.params.id - 1}`}>
<div className="DetailItem" onClick={this.showHome}>
<div className="DetailItem-image">
<img src={require(`./img/${this.props.params.id}.jpg`)} alt=""/>
</div>
Full title: {item.title}
</div>
</SharedElement>
</div>
)
}
}
SharedElement.js
import React, { Component, PropTypes, cloneElement } from 'react'
import { findDOMNode } from 'react-dom'
import TweenMax, { Power3 } from 'gsap'
export default class SharedElement extends Component {
static Store = {}
element = null
static props = {
id: PropTypes.string.isRequired,
children: PropTypes.element.isRequired,
duration: PropTypes.number,
delay: PropTypes.number,
keepPosition: PropTypes.bool,
}
static defaultProps = {
duration: 0.4,
delay: 0,
keepPosition: false,
}
storeNewPosition(rect) {
SharedElement.Store[this.props.id] = rect
}
componentDidMount() {
// Figure out the position of the new element
const node = findDOMNode(this.element)
const rect = node.getBoundingClientRect()
const newPosition = {
width: rect.width,
height: rect.height,
}
if ( ! this.props.keepPosition) {
newPosition.top = rect.top
newPosition.left = rect.left
}
if (SharedElement.Store.hasOwnProperty(this.props.id)) {
// Element was already mounted, animate
const oldPosition = SharedElement.Store[this.props.id]
TweenMax.fromTo(node, this.props.duration, oldPosition, {
...newPosition,
ease: Power3.easeInOut,
delay: this.props.delay,
onComplete: () => this.storeNewPosition(newPosition)
})
}
else {
setTimeout(() => { // Fix for 'rect' having wrong dimensions
this.storeNewPosition(newPosition)
}, 50)
}
}
render() {
return cloneElement(this.props.children, {
...this.props.children.props,
ref: element => this.element = element,
style: {...this.props.children.props.style || {}, position: 'absolute'},
})
}
}
I actually had a similar problem, where I had a search bar and wanted it to move and wrap to a different size and place on a specific route (like a general search in the navbar and a dedicated search page). For that reason, I created a component very similar to SharedElement above.
The component expects as props, a singularKey and a singularPriority and than you render the component in serval places, but the component will only render the highest priority and animate to it.
The component is on npm as react-singular-compoment
And here is the GitHub page for the docs.

Resources