react-transition-group lifecycle method componentWillLeave not being called - reactjs

I'm trying to get componentWillLeave to get called and I'm having no luck. componentWillAppear gets called, but I'm unable to get the former to call. Most responses I've seen call for making sure the callbacks are being called, which I am doing. I'm also using React router if that makes a difference. Any help would be greatly appreciated.
import React, { Component } from 'react';
import { TweenMax } from 'gsap';
import PropTypes from 'prop-types';
import { Animated } from '../../components/common/Animated';
import NavButton from '../../components/navButton/NavButton';
import './Team.scss';
class Team extends Component {
componentDidMount() {
this.el = this.refs.container;
}
componentWillAppear(callback) {
TweenMax.set(this.el, { x: '100%' });
TweenMax.to(this.el, 1, { x: '0%' });
callback();
}
componentWillLeave(callback) {
TweenMax.to(this.el, 1, { x: '-100%', onComplete: callback });
}
render() {
const { match } = this.props;
return (
<div ref="container" className="teamContainer">
<NavButton
title={'To Interactive'}
route={`/${match.params.monster}/interactive`}
/>
</div>
);
}
}
Team.propTypes = {
match: PropTypes.shape({
pathname: PropTypes.shape({
monster: PropTypes.number.isRequired,
}),
}),
};
Team.defaultProps = {
match: {
pathname: {
monster: -1,
},
},
};
export default Animated(Team);

I haven't tried this with react router, but with a similar router approach. I got it to work using TransitionGroup v1.x and made sure my active child component was
a react element
had it's own unique key
https://github.com/reactjs/react-transition-group/tree/v1-stable
Note:
When using CSSTransitionGroup, there's no way for your components to be notified when a transition has ended or to perform any more complex logic around animation. If you want more fine-grained control, you can use the lower-level TransitionGroup API which provides the hooks you need to do custom transitions.

Related

How to use leaflet-polylinedecorator in react 16.4.1

I'm trying to use the leaflet plugin polylinedecorator in react 16.4.1 (so without hooks). However, the only example I have been able to find on how to use this plugin in react is using hooks (see: How to use polylinedacorator with react leaflet), and I am unsure how I can adapt this to be able to use it in my code.
What I have so far is this polylinedecorator component:
import React, { Component } from "react";
import { Polyline } from "react-leaflet";
import L from "leaflet";
import "leaflet-polylinedecorator";
export default class PolylineDecorator extends Component {
componentDidUpdate() {
if (this.props.map) {
const polyline = L.polyline(this.props.positions).addTo(this.props.map);
L.polylineDecorator(polyline, {
patterns: [
{
offset: "100%",
repeat: 0,
symbol: L.Symbol.arrowHead({
pixelSize: 15,
polygon: false,
pathOptions: { stroke: true }
})
}
]
}).addTo(this.props.map);
}
}
render() {
return <></>;
}
}
That I am using like this:
import React, { Component } from "react";
import { Polyline, LayerGroup } from "react-leaflet";
import PolylineDecorator from "./PolylineDecorator";
export default class RouteLayer extends Component {
render() {
const { beginLocations } = this.props;
const locations = [];
const differentLocations: [];
beginLocations.forEach((location, index) => {
// some filtering going on here and pushing the locations to one of the
two arrays (locations, differentLocations)
});
return (
<LayerGroup>
<PolylineDecorator
map={this.props.map}
positions={locations}
color="#4e5c8d"
interactive={false}
/>
<PolylineDecorator
map={this.props.map}
positions={differentFloorLinesLocations}
color="red"
interactive={false}
/>
</LayerGroup>
);
}
}
The RouteLayer is nested inside the map as follows (for simplicity some components are left out):
<LeafletMap
ref={r => {
this.map = r;
if (this.props.setRefMap) {
this.props.setRefMap(r);
}
}}>
<RouteLayer
map={this.map ? this.map.leafletElement : null}
locations={locations}
/>
</LeafletMap>
Right now the polylines are drawn, however not quite as expected since the filtering doesn't seem to work (this filtering worked fine when I was just using polylines without the decorator).
The arrows I am trying to decorate the lines with are showing up, so that's good. However, I'm not happy with how the PolylineDecorator class is looking right now, this doesn't seem like the correct way to do this.
I'm also unsure if it is correct to pass the reference to the map in the way that I'm doing here.
Any suggestions on how to make this work correctly are appreciated.
For React version < 16.8 the following component demonstrates how to integrate L.polylineDecorator with React-Leaflet:
import React, { Component } from "react";
import { Polyline, withLeaflet } from "react-leaflet";
import L from "leaflet";
import "leaflet-polylinedecorator";
class PolylineDecorator extends Component {
constructor(props) {
super(props);
this.polyRef = React.createRef();
}
componentDidMount() {
const polyline = this.polyRef.current.leafletElement; //get native Leaflet polyline
const { map } = this.polyRef.current.props.leaflet; //get native Leaflet map
L.polylineDecorator(polyline, {
patterns: this.props.patterns
}).addTo(map);
}
render() {
return <Polyline ref={this.polyRef} {...this.props} />;
}
}
export default withLeaflet(PolylineDecorator);
Usage
export default class MyMap extends Component {
render() {
const { center, zoom } = this.props;
const polyline = [[57, -19], [60, -12]];
const arrow = [
{
offset: "100%",
repeat: 0,
symbol: L.Symbol.arrowHead({
pixelSize: 15,
polygon: false,
pathOptions: { stroke: true }
})
}
];
return (
<Map center={center} zoom={zoom}>
<TileLayer url="http://{s}.tile.osm.org/{z}/{x}/{y}.png" />
<PolylineDecorator patterns={arrow} positions={polyline} />
</Map>
);
}
}
Here is a demo

Navigation from within a HOC in React Native

I wish to add a check to every page in my app. The check is that if a file exists then pull the user to a page.
I think that a HOC is one way to do this (are there others?)
and I have come up with this
import React from "react";
import { NavigationScreenProp } from "react-navigation";
import RNFS from "react-native-fs";
interface MyComponentProps {
navigation: NavigationScreenProp<any, any>;
}
interface MyComponentState {}
const importFileCheck = WrappedComponent => {
class HOC extends React.Component<MyComponentProps, MyComponentState> {
constructor(props) {
super(props);
this.props.navigation.addListener("didFocus", () => {
RNFS.exists(
".\path\I\care\about.xml"
).then(exists => {
if (exists) {
this.props.navigation.navigate("Export");
}
});
});
}
render() {
return <WrappedComponent {...this.props} />;
}
}
return HOC;
};
export default importFileCheck;
When I run the page I get an error
TypeError: undefined is not an object (evaluating this.props.navigation.addListener)
So I guess that the navigation 'thing' is not being passed through properly
For completion I use the HOC like so
importFileCheck(App)
and App has the navigation stuff already in it, and works without the HOC.
Imports are
"react": "16.6.1",
"react-native": "0.57.7",
"react-navigation": "3.2.0"
Further details for the keen :D
First I make a stack navigator that is all the pages in my app
const appNav = createStackNavigator(
{
Calculator: {
screen: Calculator,
navigationOptions: { title: "Calculator" }
},
// more pages
);
export default createAppContainer(appNav);
In App.tsx
this gets 'wrapped' in other components
const WrappedStack = () => {
return <RootStack screenProps={{ t: i18n.getFixedT() }} />;
};
const ReloadAppOnLanguageChange = translate("translation", {
bindI18n: "languageChanged",
bindStore: false
})(WrappedStack);
class App extends React.Component {
public render() {
return (
<I18nextProvider i18n={i18n}>
<StyleProvider style={getTheme(material)}>
<Provider
ManureStore={ManureStore}
SettingsStore={SettingsStore}
FieldStore={FieldStore}
CalculatorStore={CalculatorStore}
FarmStore={FarmStore}
>
<ReloadAppOnLanguageChange />
</Provider>
</StyleProvider>
</I18nextProvider>
);
}
}
and finally we wrap with my new HOC
export default importFileCheck(App)
It's not easy to see what the error is when you have not provided any examples of how the component is used within react-navigation. Since the issue is related to the navigation prop not being passed it would be helpful to see a more complete example of how the HOC is used within the application, with all the react-navigation details.
That said, maybe you could try using the withNavigation HOC to ensure that the navigation prop is present. It is documented here:
https://reactnavigation.org/docs/en/connecting-navigation-prop.html
Well this defeated me (and the navigation event I wanted to use does not fire when an app returns from the background anyway)
this is my solution
import React, { Component } from "react";
import { NavigationScreenProp } from "react-navigation";
import RNFS from "react-native-fs";
import { AppState } from "react-native";
interface Props {
navigation: NavigationScreenProp<any, any>;
}
interface State {}
export default class ImportFileCheck extends Component<Props, State> {
private _handleAppStateChange = nextAppState => {
if (nextAppState === "active") {
RNFS.exists(
RNFS.DocumentDirectoryPath + "/Inbox/Import.json"
).then(exists => {
if (exists) {
this.props.navigation.navigate("Export");
}
});
}
};
constructor(props) {
super(props);
AppState.addEventListener("change", this._handleAppStateChange);
}
public componentWillUnmount() {
AppState.removeEventListener("change", this._handleAppStateChange);
}
public render() {
return null;
}
}
Then within each page files return statement I slap in a <ImportFileCheck navigation={navigation} />
What a hack!
We can use useNavigation hooks of react-navigation with version 5.x.x or later.
import { useNavigation } from '#react-navigation/native';
then initialize the navigation using useNavigation like
const navigation = useNavigation();
and then use navigation for different navigation actions like.
navigation.navigate('ToScreen');
naviagtion.goBack();
Hope this will help someone.

React - Use property from another component

I have a terminal component that render a terminale emulator.
import React, { Component } from 'react';
import { withStyles } from '#material-ui/core/styles';
import { XTerm } from '../../node_modules/react-xterm';
import os from 'os';
import { connect } from 'react-redux';
import '../../node_modules/xterm/dist/xterm.css';
const pty = require('node-pty');
const mapStateToProps = (state) => {
return state;
};
class Terminal extends Component {
constructor(props) {
super(props);
this.xtermLoaded = false;
this.ptyProcess = null;
this.term = null;
this.shell = process.env[os.platform() === 'win32' ? 'COMSPEC' : 'SHELL'];
this.ptyProcess = pty.spawn(this.shell, [], {
name: 'xterm-color',
cols: 50,
rows: 30,
cwd: process.cwd(),
env: process.env
});
}
bindXterm(xterm) {
if (!xterm || this.xtermLoaded) return;
let term = xterm.getTerminal();
if (!term.on) return;
this.term = term;
this.xtermLoaded = true;
this.term.on('data', data => {
this.ptyProcess.write(data);
});
this.ptyProcess.on('data', data => {
this.term.write(data);
});
}
render() {
return (
<XTerm style={{
addons: ['fit', 'fullscreen'],
overflow: 'hidden',
position: 'relative',
width: '100%',
height: '100%'
}} ref={xterm => this.bindXterm(xterm)}/>
);
}
}
export default connect(mapStateToProps)(withStyles(styles)(Terminal));
With this.ptyProcess.write(data), i can write new things into the terminal.
But how i can access to this.ptyProcess.write(data) from another component ?
Can someone help me please ? :)
Thanks.
Presumably this component is created either by another component, or by a call to ReactDOM.Render(). You should make ptyProcess a member of the nearest common ancestor of all the components that need to access it, and then pass it down to them as part of the props object.
As a general rule in react, if a property needs to be shared by multiple child components, then you should "hoist" that property up into the parent component and pass it down to the children through props.
You are already using redux. Just include a dataForTerminal reducer that when changes, new data are written into the terminal.
So create an action creator for it and then you can call the action creator from any component

Is there react/redux like mapStateToProps way in Vue/Vuex

Usual way to map state and actions in React/Redux looks something like this, so mapping functions are placed separately from component code:
import React, { Component } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import myAction from 'actions/request';
class MyComponent extends Component {
/* BODY */
}
function mapStateToProps(state) {
return {
myComponentProp: state.myReducer.myReducerProp
};
}
function mapDispatchToProps(dispatch) {
return {
myComponentPropAction: bindActionCreators(myAction, dispatch),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent);
The only described way to map state and actions I have found in Vue looks like this
import { mapState, mapActions } from 'vuex';
export default {
computed: {
...mapState('myReducer', {
myComponentProp: (state) => state.myReducerProp,
}),
...{
/* aditional manipulations with this.myComponentProp */
}
},
methods: {
...mapActions('myReducer', [
'myReducerAction'
]),
...{
myEventHandler: function() {
/* checke conditions before action call */
this.myReducerAction();
},
}
}
}
Because of the number of spreading, the code looks fuzzy, so the question is:
Is there a way to move mapState and mapActions outside component like in react/redux usual approach.
Thanks for help!
Okay so along with typescript support they also added in a vue-class-component decorator which could be used to achieve what your after. The link to the repository for this can be found here but I would suggest instead creating a new project via the CLI and going from there as it was added in v3 Vue Class Component Github Repository.
<script>
function Getter (getterType) {
return createDecorator((options, key) => {
if (!options.computed) options.computed = {}
options.computed[key] = function () {
return this.$store.getters[getterType]
}
})
}
import Vue from 'vue'
import Component from 'vue-class-component'
#Component({
props: {
propMessage: String
}
})
export default class App extends Vue {
#Getter('foo') bar
#Setter('psu') psi
// computed
get computedMsg () {
return 'computed ' + this.msg
}
// method
greet () {
alert('greeting: ' + this.msg)
}
}
</script>
As you can see were calling in our getters and setters using a function here which is less than optimal but is closer to a succulent answer. Now in comes the vuex-class-binding package which abstracts all of those murky functions: vuex class
import Vue from 'vue'
import Component from 'vue-class-component'
import {
State,
Getter,
Action,
Mutation,
namespace
} from 'vuex-class'
const someModule = namespace('path/to/module')
#Component
export class MyComp extends Vue {
#State('foo') stateFoo
#State(state => state.bar) stateBar
#Getter('foo') getterFoo
#Action('foo') actionFoo
#Mutation('foo') mutationFoo
#someModule.Getter('foo') moduleGetterFoo
// If the argument is omitted, use the property name
// for each state/getter/action/mutation type
#State foo
#Getter bar
#Action baz
#Mutation qux
created () {
this.stateFoo // -> store.state.foo
this.stateBar // -> store.state.bar
this.getterFoo // -> store.getters.foo
this.actionFoo({ value: true }) // -> store.dispatch('foo', { value: true })
this.mutationFoo({ value: true }) // -> store.commit('foo', { value: true })
this.moduleGetterFoo // -> store.getters['path/to/module/foo']
}
}
This is there example and it's really nice because were able to take a namespaced module and call all of it's getters and setters without any nasty custom functions and we can import all of that ready to use into a const like above. Now you'd have access to all of your modules functionality using just decorators. This is as close as it really gets to being able to assign your functionality into the component sadly, but it looks pretty nice once you've got it all setup. You can do this with or without TS I think but I've always done it in TS as it has the first class support for the vue class components which are still relatively new.

Which way should I use for Connector in Redux?

I seen 2 ways of doing the same thing but I am not sure what is the proper way.
Component
import React, {Component} from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {selectUser} from '../actions/index'
class UserList extends Component {
renderList() {
return this.props.users.map((user) => {
return (
<li
key={user.id}
onClick={() => this.props.selectUser(user)}
>
{user.first} {user.last}
</li>
);
});
}
render() {
return (
<ul>
{this.renderList()}
</ul>
);
}
}
// Get apps state and pass it as props to UserList
// > whenever state changes, the UserList will automatically re-render
function mapStateToProps(state) {
return {
users: state.users
};
}
// Get actions and pass them as props to to UserList
// > now UserList has this.props.selectUser
function matchDispatchToProps(dispatch){
return bindActionCreators({selectUser: selectUser}, dispatch);
}
// We don't want to return the plain UserList (component) anymore, we want to return the smart Container
// > UserList is now aware of state and actions
export default connect(mapStateToProps, matchDispatchToProps)(UserList);
https://github.com/buckyroberts/React-Redux-Boilerplate
Or
import React from "react"
import { connect } from "react-redux"
import { fetchUser } from "../actions/userActions"
import { fetchTweets } from "../actions/tweetsActions"
#connect((store) => {
return {
user: store.user.user,
userFetched: store.user.fetched,
tweets: store.tweets.tweets,
};
})
export default class Layout extends React.Component {
componentWillMount() {
this.props.dispatch(fetchUser())
}
fetchTweets() {
this.props.dispatch(fetchTweets())
}
render() {
const { user, tweets } = this.props;
if (!tweets.length) {
return <button onClick={this.fetchTweets.bind(this)}>load tweets</button>
}
const mappedTweets = tweets.map(tweet => <li>{tweet.text}</li>)
return <div>
<h1>{user.name}</h1>
<ul>{mappedTweets}</ul>
</div>
}
}
https://github.com/learncodeacademy/react-js-tutorials/tree/master/5-redux-react
The first way uses 2 different functions mapStateToProps() and matchDispatchToProps() while the other way uses #connect(....).
When I use the #connect I get a whole bunch of warnings saying that it has not been finalized and might change.
The # symbol is a decorator which is still considered experimental. So I would use that at your own risk. Your first code block is the safer way to do it as described in the official docs. Both blocks essentially do the same thing but decorators are more sugar than anything.
References:
https://github.com/reactjs/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options
What's the '#' (at symbol) in the Redux #connect decorator?
I think the first method will give you less problems in the end. Someone else can chime in though too.
The answer by Jackson is right in every sense however he is missing out the importance of using the first version for the usage of unit testing. If you want to be able to unit test a component (which usually means testing with the unconnected version) you need to be able to export the connected and unconnected component.
Using your example and assuming you are using jest/enzyme you could do something like this:
// notice importing the disconnected component
import { UserList } from '../relative/file/path/UserList'
import { mount } from 'enzyme'
describe('UserList', () => {
it('displays the Username', () => {
const users = [{fist: 'Person', last: 'Thing'}, ... ]
const UserList = mount(<UserList users={users} />)
export(UserList.find('li')[0].text()).toEqual('Person Thing')
});
});
Once you build larger projects being able to unit test will provide sanity to your coding life. Hope this helps

Resources