Having trouble using React Transition Group first time - reactjs

Good day! I wrote this code to test the React transition group library and eventually get stuck with the error. The script gets run and I see the data fill form on the page styled but when I click submit button form does not disappear. Error reference description: Failed prop type: The prop timeout is marked as required in CSSTransition, but its value is undefined.
in CSSTransition (at app.jsx:24)
in App (at index.js:7) However transitionAppearTimeot={1500}!
import React, { Component } from 'react';
import CSSTransitionGroup from 'react-transition-group/CSSTransition';
import './app.css';
import Form from './components/Form';
class App extends Component {
constructor() {
super();
this.state = {
mounted: true,
};
this.handleSubmit = this.handleSubmit.bind(this);
};
handleSubmit(event) {
event.preventDefault();
this.setState = {
mounted: false
}
console.log(this.state);
};
render() {
return (
<div className="app">
<CSSTransitionGroup
transitionName="fade"
transitionAppear={true}
transitionAppearTimeout={1500}
transitionEnter={false}
transitionLeave={true}
transitionLeaveTimeout={300}>
{this.state.mounted && <Form onSubmit=
{this.handleSubmit} />}
</CSSTransitionGroup>
</div>
);
}
}
export default App;

The error message is very specific. <CSSTransition> requires a prop timeout but you do not pass anything. Also your other props are not what <CSSTransition> expects.
I guess you are mixing up the old react-transition-group v1 with the new react-transition-group v2. You are using v2 which has a totally different API.

Related

How to check whether React updates dom or not?

I wanted to check how to react does reconciliation so I updated the inner HTML of id with the same text. Ideally, it shouldn't update the dom but it is paint reflashing in chrome.
I have tried paint reflashing in chrome it is showing green rectangle over that same text
import React from 'react';
function App() {
return (
<div >
<p id="abc" key="help">abc is here</p>
<button onClick={function () {
// document.getElementById("may").innerHTML = "";
document.getElementById("abc").innerHTML = "abc is here";
}} > Btn</button>
</div>
);
}
export default App;
Expected result should be that paint reflashing shouldn't happen but it is happening.
You are not using React here to update the text of your p tag but directly updating the DOM with JavaScript.
So React reconciliation algorithm doesn't even run here.
In React, the output HTML is a result of the state and the props of your component.
When a change in state or props is detected, React runs the render method to check if it needs to update the DOM. So, in order to do this check, you need to store the parameters that determine your view in state or props.
Given your example, we could save the text you want to show in the p tag in the state of your component (using hooks):
import React, { useState } from 'react';
function App () {
const [text, setText] = useState('abc is here');
render() {
return (
<div >
<p id="abc" key="help">{this.state.text}</p>
<button onClick={() => setText('abc is here') }>Btn</button>
</div>
);
}
}
export default App;
If you are using a version of React that does not support hooks, you will need to transform your functional component into a class to use state:
import React, { Component } from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = { text: 'abc is here' };
}
render() {
return (
<div >
<p id="abc" key="help">{this.state.text}</p>
<button onClick={() => this.setState({ text: 'abc is here' }) }>Btn</button>
</div>
);
}
}
export default App;

Which is the react way of complex conditional rendering?

Issue
I have a problem with conditional rendering of a component. As far as I can see, there are 2 approaches to doing this. First approach is ugly as it becomes difficult when I have to do multiple && conditions. The second way is clear, but it adds the component itself to the state and further computations with the state value is difficult. E.g checking what is the message value for error.
I have given both the approaches below. Please let me know which would be better. Is there a another approach than both of them?
Application
This is a simple application that renders either 'Main' component or 'Err' component, based on the state of 'err' attribute in first approach and content of the comp attribute in second approach.
Initially Main component is rendered. The err attribute is updated to some value after 2 seconds, which triggers rerendering. At this time, I want Err component to render.
The real application is I have an external api call on componentDidMount and it can either fail or succeed. I have to display different components based on result. It is a little more complicated with multiple state values being updated. I have simplified the issue below for the purpose of demonstration.
Common steps for both types
npx create-react-app react-oop
component/Err.js
import React,{Component} from 'react'
class Err extends Component {
render(){
return(
<div>
Error Component
</div>
)
}
}
export default Err
component/Main.js
import React, {Component} from 'react'
class Main extends Component {
render(){
return(
<div>
Main Component
</div>
)
}
}
export default Main
First approach
App.js
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import Err from './components/Err'
import Main from './components/Main'
class App extends Component {
constructor(props){
super(props)
this.state = {
err: null
}
this.setError = this.setError.bind(this)
}
setError(){
return(
this.setState(() => {
return({
err: 'Error'
})
})
)
}
componentDidMount(){
setTimeout(this.setError, 2000)
}
render() {
return (
<div className="App">
{
this.state.err ? <Err /> : <Main />
}
</div>
);
}
}
export default App;
Second approach
App.js
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import Err from './components/Err'
import Main from './components/Main'
class App extends Component {
constructor(props){
super(props)
this.state = {
comp: <Main />
}
this.setError = this.setError.bind(this)
}
setError(){
return(
this.setState(() => {
return({
comp: <Err />
})
})
)
}
componentDidMount(){
setTimeout(this.setError, 2000)
}
render() {
return (
<div className="App">
{this.state.comp}
</div>
);
}
}
export default App;
I definitely recommend the 1st approach. Store data (json), not views (jsx) in your component's state.
Actually there is a 3rd approach that takes the best of both:
use a jsx variable to edit the view (with your logic) before rendering
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import Err from './components/Err';
import Main from './components/Main';
class App extends Component {
constructor(props){
super(props);
this.state = {
err: null
};
}
// This way of writing functions saves you the binding
setError = () => this.setState({err: 'Error'})
componentDidMount(){
setTimeout(this.setError, 2000);
}
render() {
let comp = <Main />;
// Put your logic here so your returned JSX is clear
if (this.state.err)
comp = <Err />;
return (
<div className="App">
{comp}
</div>
);
}
}
export default App;
Both approaches are essentially the same but I prefer option 1 as it's simpler to grasp. You can also use something like babel-plugin-jsx-control-statements#choose which makes the React component look simpler:
<Choose>
<When condition={ test1 }>
<Main />
</When>
<When condition={ test2 }>
<AnotherMain />
</When>
<Otherwise>
<Err />
</Otherwise>
</Choose>

ReactJS - JSON objects in arrays

I am having a little problem and can't seem to understand how to fix it. So I am trying to create a pokemon app using pokeapi. The first problem is that I can't get my desired objects to display. For example I want to display {pokemon.abilities[0].ability}, but it always shows Cannot read property '0' of undefined but just {pokemon.name} or {pokemon.weight} seems to work. So the problem appears when there is more than 1 object in an array.
import React, {Component} from 'react';
export default class PokemonDetail extends Component{
constructor(props){
super(props);
this.state = {
pokemon: [],
};
}
componentWillMount(){
const { match: { params } } = this.props;
fetch(`https://pokeapi.co/api/v2/pokemon/${params.id}/`)
.then(res=>res.json())
.then(pokemon=>{
this.setState({
pokemon
});
});
}
render(){
console.log(this.state.pokemon.abilities[0]);
const { match: { params } } = this.props;
const {pokemon} = this.state;
return (
<div>
{pokemon.abilities[0].ability}
<img src={`https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${params.id}.png`} />
</div>
);
}
}
And also some time ago I added the router to my app, so I could pass id to other components, but the thing is I want to display pokemonlist and pokemondetail in a single page, and when you click pokemon in list it fetches the info from pokeapi and display it in pokemondetail component. Hope it makes sense.
import React, { Component } from 'react';
import { BrowserRouter as Router, Route} from 'react-router-dom';
import './styles/App.css';
import PokemonList from './PokemonList';
import PokemonDetail from './PokemonDetail';
export default class App extends Component{
render(){
return <div className="App">
<Router>
<div>
<Route exact path="/" component={PokemonList}/>
<Route path="/details/:id" render={(props) => (<PokemonDetail {...props} />)}/>
</div>
</Router>
</div>;
}
}
In case componentWillMount(), An asynchronous call to fetch data will not return before the render happens. This means the component will render with empty data at least once.
To handle this we need to set initial state which you have done in constructor but it's not correct. you need to provide default values for the abilities which is an empty array.
So change it to
this.state = {
pokemon: {
abilities: []
}
}
And then inside render method before rendering you need to verify that it's not empty
render() {
return (
(this.state.pokemon.abilities[0]) ?
<div>
{console.log(this.state.pokemon.abilities[0].ability)}
<img src={`https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/1.png`} />
</div> :
null
);
}
It is common in React that you always need to safe-check for existence of data before rendering, especially when dealing with API data. At the time your component is rendered, the state is still empty. Thus this.state.pokemon.abilities is undefined, which leads to the error. this.state.pokemon.name and this.state.pokemon.weight manage to escape same fate because you expect them to be string and number, and don't dig in further. If you log them along with abilities, they will be both undefined at first.
I believe you think the component will wait for data coming from componentWillMount before being rendered, but sadly that's not the case. The component will not wait for the API response, so what you should do is avoid accessing this.state.pokemon before the data is ready
render(){
const { match: { params } } = this.props;
const {pokemon} = this.state;
return (
<div>
{!!pokemon.abilities && pokemon.abilities[0].ability}
<img src={`https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${params.id}.png`} />
</div>
);
}

React state not updating

So I'm making a simple React application that needs to show a modal when something is clicked. I use react-modal to achieve this and the modal is showing but I'm not able to close it again. Here's the code:
import React from 'react';
import ReactDOM from 'react-dom';
import Paper from "./Paper";
ReactDOM.render(
<Paper title={"Title"} notes={"Notes"}/>,
document.getElementById('root')
);
and the Paper definition:
import React, {Component} from 'react';
import Modal from 'react-modal';
class Paper extends Component {
constructor(props) {
super(props);
this.state = {modalIsOpen: false};
this.showModal = this.showModal.bind(this);
this.closeModal = this.closeModal.bind(this);
}
showModal() {
this.setState({modalIsOpen: true});
}
closeModal() {
this.setState({modalIsOpen: false});
}
render() {
return (
<div onClick={this.showModal}>
{this.props.title}
<Modal isOpen={this.state.modalIsOpen}>
<button onClick={this.closeModal}>close</button>
{this.props.notes}
</Modal>
</div>
);
}
}
The state just doesn't get updated if I check it in the developer tools and I have no idea why. Anyone who can help me?
I was able to reproduce your problem, which was actually a bit strange - even if I logged this.state.modalIsOpen in the setState callback after it was set to false, the value was still true. At any rate, I changed the code to perform a toggle instead and it resolved the issue:
https://codesandbox.io/s/q38nzl9yy9
toggleModal() {
this.setState({ modalIsOpen: !this.state.modalIsOpen });
}
render() {
return (
<div onClick={this.showModal}>
{this.props.title}
<Modal isOpen={this.state.modalIsOpen}>
<button onClick={this.closeModal}>close</button>
{this.props.notes}
</Modal>
</div>
);
}
I'm still digging into why the this context seems to be getting muddled, since this appears to be a straightforward example.

React - Route with same path, but different parameters change data on update

I am creating a page where you can see peoples profiles and all their items, which has pathname="/users/{their id}" and a menu where it can take you to your profile. But my problem is that when you go to a persons profile page, and then to another one, the pathname changes but the data does not get rendered it only changes the pathname and the data remains the same. In order to render the data, you would have to refresh the page and then it shows the new users data. How would I get it so you wouldn't have to refresh the page, so like they click on the user they want to go to, the pathname changes and renders the new data without the page refresh? Also, something happens when you refresh the page when on a user profile, it is supposed to return the users' email address, which it does when you first visit the page, but when you refresh the page it returns an error saying it can't find the email.
Here is the code for the menu part (link to my profile):
import { Meteor } from "meteor/meteor"
import React from "react";
import { withRouter, Link } from "react-router-dom";
import { SubjectRoutes } from "./subjectRoutes/subjectRoutes";
import AddNote from "./AddNote";
class Menu extends React.Component{
render(){
return(
<div>
<div className="scroll"></div>
<div className="menu">
<h1>Menu</h1>
<p><Link to="/">Home</Link></p>
<Link to="/searchNotes">Notes</Link>
<p><Link to="/addNote">Add a Note</Link></p>
<p><Link to={`/users/${Meteor.userId()}`} >My Profile</Link></p>
</div>
</div>
)
}
}
export default withRouter(Menu)
userProfile.js:
import { Meteor } from "meteor/meteor";
import React from "react";
import { withRouter } from "react-router-dom";
import { Tracker } from "meteor/tracker";
import Menu from "./Menu";
import RenderNotesByUserId from "./renderNotesByUserId"
class userProfile extends React.Component{
constructor(props){
super(props);
this.state = {
email: ""
};
}
logoutUser(e){
e.preventDefault()
Accounts.logout(() => {
this.props.history.push("/login");
});
}
componentWillMount() {
Meteor.subscribe('user');
Meteor.subscribe('users');
this.tracker = Tracker.autorun(() => {
const user = Meteor.users.findOne(this.props.match.params.userId)
this.setState({email: user.emails[0].address})
});
}
render(){
return(
<div>
<Menu />
<button onClick={this.logoutUser.bind(this)}>Logout</button>
<h1>{this.state.email}</h1>
<RenderNotesByUserId filter={this.props.match.params.userId}/>
</div>
)
}
}
export default withRouter(userProfile);
Sorry to make this question so long it's just a really weird problem that I am having which I can't seem to find any answers to online.
ComponentWillMount() only runs one time, before your component is rendered. You need to also use ComponentWillReceiveProps() in order to update your state when your props change.
Check out React Component Lifecycle
you can use useLocation in this situation.
let location = useLocation();
useEffect(() => {
dispatch(fetchDetail(id))
dispatch(fetchSuggestions(category))
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [location]);

Resources