React: Spring transition not behaving properly - javascript

I recently got started with both React and JavaScript. I've been reading the Spring API docs for the Transition component, and ended up with this piece of code when doing some tests. Now, I expected a smooth transition, but got the following sequence:
When switch changes, the mounted element abruptly appears.
Some time goes by (this is dependent on the config that I choose)
The one unmounted disappears (abruptly as well)
For what I read in the docs, I'm not able to see what I'm doing wrong here, so any help is more than welcome. I've tested different configs and always get that sequence.
PS: Content is just a plain div that takes its background color as prop, so nothing fancy there.
import React from 'react';
import './App.css';
import Content from './components/content';
import {Transition, config} from 'react-spring/renderprops';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
switch: false
};
}
render() {
return (
<div id="container" onClick={() => {this.setState({switch: !this.state.switch})}}>
<Transition
config={config.slow}
items={this.state.switch}
from={{opacity: 0}}
enter={{opacity: 1}}
leave={{opacity: 0}}>
{s => s ? () => <Content backgroundColor="rgb(37, 95, 142)"></Content>
: () => <Content backgroundColor="rgb(37, 142, 118)"></Content>}
</Transition>
</div>
);
}
}
export default App;
Thanks!

The Transtition changes the opacity in your example, but you never give the changed values to you rendered component. It missing the prop parameter in the arrow function. You should add this prop to the style property to your component.
For example:
props => (<Content props={props} backgroundColor="rgb(37, 95, 142)" />)
And the Component is something like this:
const Content = ({ backgroundColor, props }) => (
<animated.div style={{ backgroundColor, ...props }}>Content</animated.div>
);
And now you can see the opacity change.
I also used animated.div here and added the native poperty to Transition for better performance.
Here is my example:
https://codesandbox.io/s/ecstatic-wood-7p1ym?file=/src/App.js

Related

How to call returned const from a react function in a class based component

Here I have a Loading screen as a functional react component that I try to render conditionally in the App component.
The concept of this loading screen is that I have a boolean variable that will be used to conditionally render the home page after the loading screen ends.
import React from 'react';
import { useState, useEffect } from 'react';
import { useSpring, animated } from 'react-spring';
import BarLoader from 'react-spinners/BarLoader';
import Logo from "../assets/images/logo.svg";
const LoadingScreen = () => {
const spinner = `
display: block;
margin: 0 auto;
width: 150px;
height: 2.5px;
`;
const style = useSpring({opacity: 1, from: {opacity: 0}});
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
setIsLoading(true);
setTimeout(() => {
setIsLoading(false)
}, 4000)
}, [])
const LoadingTemplate = () => {
<animated.div className="loading-screen" style={style}>
<div className="loader-wrapper">
<img className="splash-logo" src={Logo} alt="Marouane Edghoughi" />
<BarLoader color="#384BEB" css={ spinner } loading={isLoading} />
</div>
</animated.div>
}
return { LoadingTemplate, isLoading }
}
export default LoadingScreen;
When I try to call the boolean variable and the screen template in the following code:
render() {
const {LoadingTemplate, isLoading} = LoadingScreen();
return (
<Router>
{
isLoading ?
<LoadingTemplate />
:
<Container className="theme p-0" fluid={true}>
{/* something will be displayed here */}
</Container>
}
</Router>
);
}
}
I just get this error:
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
The function is working properly if I try to call it from a functional component. This is my first time trying it with a class.
Any help would be greatly appreciated ^_^
The error message is 100% correct. Hooks can be used only in Function Components, and cannot be used like this in class components. The underlying mechanics of the two types are different. Hooks are a feature of functional components and rely on those mechanics, and are therefore not compatible with class components.
You may not realize that you are using a hook, but LoadingScreen actually is one: It returns a value other than a React Element, and you are calling it as a function (i.e. const x = LoadingScreen()), rather than using it as a component (i.e. <LoadingScreen />).
That's definitely not allowed in class components. You could use a function component instead:
const Component = () => {
const {LoadingTemplate, isLoading} = LoadingScreen();
return (
<Router>
{
isLoading ?
<LoadingTemplate />
:
<Container className="theme p-0" fluid={true}>
{/* something will be displayed here */}
</Container>
}
</Router>
);
}
}
Or you can try these methods to get around this limitation. If you do decide to use a function component instead, then you should use useLoadingScreen to follow the React hook naming conventions.

React Testing Library, Component Unit Tests

I am trying to build a test unit for my simple React Application using React Testing Library. I readed all docs and get stuck in it.
API was created by create React app. One of the feature is that user can change theme. There is setTheme hook that going to change theme "dark" and "light".
App.js
const App = () => {
const [theme, setTheme] = useState('dark');
return ( <div>
<Header theme={theme} setTheme={setTheme} />
</div>)
};
Header.js
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
const Header = props => {
return (
<header data-testid="header">
<h1><span className="highlight">Github Users</span></h1>
{props.theme === "dark" ?
<FontAwesomeIcon data-testid="button" icon="sun" size="2x" color="#dcba31" onClick={ () => props.setTheme('light') }/>
: <FontAwesomeIcon icon="moon" size="2x" color="#1c132d" onClick={ () => props.setTheme('dark') }/>}
</header>
);
}
export default Header;
In Header component I added arrow function that changes color of theme.
Now I am trying to write a test that's gonna test Header Component.
Expected result is that after first render Header component shall render icon "sun".
After user click on it header shall return icon "moon".
There is something that i try but it's not working as I mention.
Header.test.js
import React from 'react';
import { render, cleanup } from "#testing-library/react"
import '#testing-library/jest-dom/extend-expect';
import { act } from "react-dom/test-utils";
import Header from '../components/Header';
afterEach(cleanup);
describe("Header Component", () => {
it("first render should return a sun icon", () => {
const {getByTestId } = render(<Header />)
expect(getByTestId("header"). // What method uses here? To check if it is icon sun or moon ? )
})
it("after mouse click event should return a moon icon", () => {
const button = document.querySelector("svg"); // it is correct way to add a svg element as a button ?
act( () => {
button.dispatchEvent(new MouseEvent('click', {bubbles: true}));
})
expect(getByTestId("header"). // again what to write here to test it after click ?
})
})
I am sure that there is some other way to check first render and then after click what's Header component rendering. I think that problem is that there is another Component that is rendered conditionaly. If it is text there is no problem, but after render there is svg element with some attributes like icon="sun" / icon="moon".
Live version of project
Github Repo Link
Questions:
How to properly test that Header component ?
How to pass props in test for example I want to use that setTheme hook in test how to do it ?
There's many ways to do this and I can recommend the articles here https://kentcdodds.com/blog/?q=test to get you started. As for your current set-up I'd change some stuff that I find helpful writing unit tests:
Use data-testid to find elements, e.g. "first render should return a sun icon" can be confirmed by expect(getByTestId('svg-sun')).toBeDefined(), which is a pattern I like
Structure your it's like stubbing-rendering-assertions and only test one thing in each test, for instance, in your second it you're lacking a rendering part of the test
Regarding your question regarding passing the props, you can pass it as render(<Header theme={theme} setTheme={setThemeStub}/>) where const setThemeStub = jest.fn(). This allows you to make assertions as expect(setThemeStub).toBeCalledWith(...)

ReactJS - Disabling a component

I need to disable PostList component in its initial state.
import React from 'react';
import PostList from './PostList';
const App = () => {
return (
<div className="ui container">
<PostList />
</div>
);
};
export default App;
Whats the best way to disable (and grey out) a component? Possible solutions are to pass a value as props and then apply it to a ui element, However please keep in mind that PostList may have inner nested components as well. Please share an example.
Since you mentioned in a comment that instead of hiding it, you want to grey it instead. I would use the disabled state and style the component. Since PostList could be nested, we don't know what the props are since you did not specify them.
Also, I assuming that you are not using styled-components.
import React, { useState } from "react";
import PostList from "./PostList";
const App = () => {
const [disabled, setDisabled] = useState(true);
return (
<div className="ui container">
<PostList
style={{
opacity: disabled ? 0.25 : 1,
pointerEvents: disabled ? "none" : "initial"
}}
/>
</div>
);
};
export default App;
There are 2 different ways I like to do something like this.
One way you can do it is by using state
this.state = {
showList: false
}
and than something like
return (
{this.state.showList && <PostList />}
)
Another option is to pass the showList in state as a prop, so something like
return(
<PostList show = {this.state.showList} />
)
and than in PostList something like
return props.show && (your component here)
You can also use conditional classNames, so if you want that component shown, you can throw a className and style it how you normally would, but if not, just throw a display: none. I usually save doing that for replacing a navbar with a dropdown button on smaller screens, but it is another option

infinite Render in React

I am having problem figuring out why my application is doing endless render.
Inside, My stateful component, I am calling a redux action in componentDidMount method (calling componentWillMount also do endless render)
class cryptoTicker extends PureComponent {
componentDidMount() {
this.props.fetchCoin()
// This fetches some 1600 crypto coins data,Redux action link for the same in end
}
render() {
return (
<ScrollView>
<Header />
<View>
<FlatList
data={this.state.searchCoin ? this.displaySearchCrypto : this.props.cryptoLoaded}
style={{ flex: 1 }}
extraData={[this.displaySearchCrypto, this.props.cryptoLoaded]}
keyExtractor={item => item.short}
initialNumToRender={50}
windowSize={21}
removeClippedSubviews={true}
renderItem={({ item, index }) => (
<CoinCard
key={item["short"]}
/>
)}
/>
</View>
</ScrollView>
)
}
}
In CoinCard I am literally doing nothing besides this (Notice CoinCard inside Flat list)
class CoinCard extends Component {
render () {
console.log("Inside rende here")
return (
<View> <Text> Text </Text> </View>
)
}
}
Now, When I console log in my coincard render, I can see infinite log of Inside rende here
[Question:] Can anyone please help me figure out why this could be happening?
You can click here to see my actions and click here to see my reducer.
[Update:] My repository is here if you want to clone and see it by yourself.
[Update: 2]: I have pushed the above shared code on github and it will still log endless console.log statements (if you can clone, run and move back to this commit ).
[Update:3]: I am no longer using <ScrollView /> in <FlatList /> also when I mean endless render, I mean is that it is endless (& Unecessarily) passing same props to child component (<Coincard />), if I use PureComponent, it won't log endlessly in render () { but in componentWillRecieveProps, If I do console.log(nextProps), I can see the same log passed over and over again
There are some points to note in your code.
The CoinCard Component must be a PureComponent, which will not re-render if the props are shallow-equal.
You should not render your Flatlist inside the ScrollView component, which would make the component render all components inside it at once which may cause more looping between the Flatlist and ScrollView.
You can also a definite height to the rendered component to reduce the number of times component is rendered for other props.
Another thing to note is, only props in the component are rendered on scroll bottom, based on the log statement mentioned below.
import {Dimensions} from 'react-native'
const {width, height} = Dimensions.get('window)
class CoinCard extends React.PureComponent {
render () {
console.log(this.props.item.long) //... Check the prop changes here, pass the item prop in parent Flatlist. This logs component prop changes which will show that same items are not being re-rendered but new items are being called.
return (
<View style={{height / 10, width}}> //... Render 10 items on the screen
<Text>
Text
</Text>
</View>
)
}
}
UPDATE
This extra logging is due to the props being from the Flatlist to your component without PureComponent shallow comparison.
Note that componentWillReceiveProps() is deprecated and you should avoid them in your code.
React.PureComponent works under the hood and uses shouldComponentUpdate to use shallow comparison between the current and updated props. Therefore log console.log(this.props.item.long) in your PureComponent' render will log the unique list which can be checked.
Like izb mentions, the root cause of the pb is the business call that is done on a pure component whereas it is just loaded. It is because your component make a business decision (<=>"I decide when something must be showed in myself"). It is not a good practice in React, even less when you use redux. The component must be as stupid a possible and not even decide what to do and when to do it.
As I see in your project, you don't deal correctly with component and container concept. You should not have any logic in your container, as it should simply be a wrapper of a stupid pure component. Like this:
import { connect, Dispatch } from "react-redux";
import { push, RouterAction, RouterState } from "react-router-redux";
import ApplicationBarComponent from "../components/ApplicationBar";
export function mapStateToProps({ routing }: { routing: RouterState }) {
return routing;
}
export function mapDispatchToProps(dispatch: Dispatch<RouterAction>) {
return {
navigate: (payload: string) => dispatch(push(payload)),
};
}
const tmp = connect(mapStateToProps, mapDispatchToProps);
export default tmp(ApplicationBarComponent);
and the matching component:
import AppBar from '#material-ui/core/AppBar';
import IconButton from '#material-ui/core/IconButton';
import Menu from '#material-ui/core/Menu';
import MenuItem from '#material-ui/core/MenuItem';
import { StyleRules, Theme, withStyles, WithStyles } from '#material-ui/core/styles';
import Tab from '#material-ui/core/Tab';
import Tabs from '#material-ui/core/Tabs';
import Toolbar from '#material-ui/core/Toolbar';
import Typography from '#material-ui/core/Typography';
import AccountCircle from '#material-ui/icons/AccountCircle';
import MenuIcon from '#material-ui/icons/Menu';
import autobind from "autobind-decorator";
import * as React from "react";
import { push, RouterState } from "react-router-redux";
const styles = (theme: Theme): StyleRules => ({
flex: {
flex: 1
},
menuButton: {
marginLeft: -12,
marginRight: 20,
},
root: {
backgroundColor: theme.palette.background.paper,
flexGrow: 1
},
});
export interface IProps extends RouterState, WithStyles {
navigate: typeof push;
}
#autobind
class ApplicationBar extends React.PureComponent<IProps, { anchorEl: HTMLInputElement | undefined }> {
constructor(props: any) {
super(props);
this.state = { anchorEl: undefined };
}
public render() {
const auth = true;
const { classes } = this.props;
const menuOpened = !!this.state.anchorEl;
return (
<div className={classes.root}>
<AppBar position="fixed" color="primary">
<Toolbar>
<IconButton className={classes.menuButton} color="inherit" aria-label="Menu">
<MenuIcon />
</IconButton>
<Typography variant="title" color="inherit" className={classes.flex}>
Title
</Typography>
<Tabs value={this.getPathName()} onChange={this.handleNavigate} >
{/* <Tabs value="/"> */}
<Tab label="Counter 1" value="/counter1" />
<Tab label="Counter 2" value="/counter2" />
<Tab label="Register" value="/register" />
<Tab label="Forecast" value="/forecast" />
</Tabs>
{auth && (
<div>
<IconButton
aria-owns={menuOpened ? 'menu-appbar' : undefined}
aria-haspopup="true"
onClick={this.handleMenu}
color="inherit"
>
<AccountCircle />
</IconButton>
<Menu
id="menu-appbar"
anchorEl={this.state.anchorEl}
anchorOrigin={{
horizontal: 'right',
vertical: 'top',
}}
transformOrigin={{
horizontal: 'right',
vertical: 'top',
}}
open={menuOpened}
onClose={this.handleClose}
>
<MenuItem onClick={this.handleClose}>Profile</MenuItem>
<MenuItem onClick={this.handleClose}>My account</MenuItem>
</Menu>
</div>
)}
</Toolbar>
</AppBar>
</div >
);
}
private getPathName(): string {
if (!this.props.location) {
return "/counter1";
}
return (this.props.location as { pathname: string }).pathname;
}
private handleNavigate(event: React.ChangeEvent<{}>, value: any) {
this.props.navigate(value as string);
}
private handleMenu(event: React.MouseEvent<HTMLInputElement>) {
this.setState({ anchorEl: event.currentTarget });
}
private handleClose() {
this.setState({ anchorEl: undefined });
}
}
export default withStyles(styles)(ApplicationBar);
Then you will tell me: "but where do I initiate the call that will fill my list?"
Well I see here that you use redux-thunk (I prefer redux observable... more complicated to learn but waaaaaaaaaaaaaaaaay more powerful), then this should be thunk that initiates the dispatch of this!
To summarize:
Components: the stupidest element that normally should have only the render method, and some other method handler to bubble up user events. This method only takes care of showing its properties to the user. Don't use the state unless you have a visual information that belongs only to this component (like the visibility of a popup for example). Anything that is showed or updated comes from above: a higher level component, or a container. It doesn't decide to update its own values. At best, it handles a user event on a subcomponent, then bubble up another event above, and... well maybe at some point, some new properties will be given back by its container!
Container: very stupid logic that consists in wrapping a top level component into redux for it to plug events to actions, and to plug some part of the store to properties
Redux thunk (or redux observable): it is the one that handles the whole user application logic. This guy is the only one who knows what to trigger and when. If a part of your front end must contain the complexity, it's this one!
Reducers: define how to organize the data in the store for it to be as easily usable as possible.
The store: ideally one per top level container, the only one that contains the data that must be showed to the user. Nobody else should.
If you follow these principles, you should never face any issue like "why the hell this is called twice? and... who made it? and why at this moment?"
Something else: if you use redux, use an immutability framework. Otherwise you may face issues as reducers must be pure functions. For this you can use a popular one immutable.js but not convenient at all. And the late ousider that is actually a killer: immer (made by the author or mobx).
It seems Jacob in the above comment has managed to make the component render only twice.
This will definitely cause double initial render (and would cause an infinite render if it wasn't a PureComponent):
componentDidUpdate() {
var updateCoinData;
if (!updateCoinData) { // <- this is always true
updateCoinData = [...this.props.cryptoLoaded];
this.setState({updateCoinData: true}); // <- this will trigger a re render since `this.state.updateCoinData` is not initially true
}
...
}
Link to the issue in your repository

Incorrect casing error with dynamically rendered component in React

So, i’d like to spare time later and want to do a dynamically generated page. For that reason i want to read component data from an object, like this:
layout: {
toolbar: {
components: [
{
type: "Testcomp",
theme: "default",
data: "div1"
},
{
type: "Testcomp",
theme: "pro",
data: "div2"
},
]}}
The component would be dynamically imported, enabled/activated and besides that this is the jsx code supposed to render components dynamically:
render() {
const toolbarComponents = userSession.layout.toolbar.components.map(Component => (
<Component.type theme={Component.theme} data={Component.data} key={this.getKey()} />
));
return (
<div>
<div className="toolbar">
toolbar
{toolbarComponents}
</div>
. . .
</div>
);
}
However i get the following warning in Chrome’s devtool, also the component is not displayed:
Warning: is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements.
Warning: The tag is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter.
What’s wrong with my code?
You are getting those errors because you are not referencing the component itself here, instead using a string as name. So, maybe you need to think another way to create the components dynamically. Like starting with a base component and only give some props and data to it.
// Define above main component or elsewhere then import.
const MyComponent = ( props ) => <div>{props.data}</div>
// Main component
render() {
const toolbarComponents = userSession.layout.toolbar.components.map(
component => <MyComponent theme={component.theme} data={component.data} />
);
return <div className="App">{toolbarComponents}</div>;
}
Here we are not using a type key anymore. If you want to use different components like that, you can create every base component and then use as its name with type key but not with string, directly referencing the component.
I tried using the same method as you, wherein I was using a string to reference a React component by name. However, it doesn't appear designed to be used outside of standard tags like div.
Instead, what worked for me is to import the component I wanted to show and then set that in the state.
import MyComponent from 'mycomponent';
class Parent extends React.Component {
constructor() {
super();
this.state = {
selectedComponent: null
};
}
render() {
return (
<div className="Parent">
<h2>Parent Component</h2>
<button onClick={() => this.setState({ selectedComponent: MyComponent })}>Show my component</button>
{this.state.selectedComponent !== null && React.createElement(this.state.selectedComponent, null, null)}
</div>
);
}
}

Categories

Resources