React infinite render loop - javascript

I have created a component which needs to be individually displayed in multiple parts of the application - (inside of my navigation, on the home page and on a separate route). Therefore, I made all my action dispatches (api calls for reading data) inside a useEffect in this component:
useEffect(() => {
dispatch(time.read())
dispatch(pause.read())
dispatch(travel.read())
dispatch(distances.read())
}, [])
As you can see, this effect should only run once (when the component mounts).
Nowhere else do I dispatch these specific actions inside or outside of this component.
This component is rendered in 3 places (inside of the nav, in its own /time route and inside of the home component). I can render it inside of the navigation and on /time at the same time without a problem. It acts as it should (when I make changes from the navigation, they are mirrored on /time since it is all tied together with redux). Finally there is the Home component.
When I first tried to render on the Home component, it took a while to load, then I tried opening it up from my mobile device through my v4 address (same network), it took too long to load. At this point my laptop started sounding like a helicopter, so I opened htop, memory usage and CPU usage was pretty much maxed out, I opened up my server logs and saw hundreds of requests per second. Here is how I render this component inside my Home component:
import React from "react"
import { useSelector } from "react-redux"
import { NavLink } from "react-router-dom"
import "./Home.css"
import {
FaCarCrash,
CgProfile,
AiOutlineShop,
TiVideo,
} from 'react-icons/all'
import Tracking from "../TimeTracking/Tracking" /*This is the component which causes all the toruble*/
export default function Home(props) {
const current = useSelector(state => state.user.currentUser)
return (
<div className="Home container">
<div id="links">
<NavLink to="/create_job/">
<FaCarCrash />
<p> CREATE JOB </p>
</NavLink>
<NavLink to={"/profile/" + current._id}>
<CgProfile />
<p> PROFILE </p>
</NavLink>
<NavLink to="/shop/view/">
<AiOutlineShop />
<p> SHOP </p>
</NavLink>
<NavLink to="/tutorials/">
<TiVideo />
<p> TRAINING </p>
</NavLink>
</div>
<Tracking /> /*This is the component which causes all the toruble*/
</div>
)
}
As you can see, nothing out of the ordinary - a simple functional component which doesn't even dispatch any actions. Only communication with redux here is the useSelector.
Here is where I render the Home component:
import React, { Fragment } from 'react'
import { Route } from 'react-router-dom'
/*...other imports...*/
import Home from "../components/Mobile/Home"
/*...other imports...*/
export default props => (
<Fragment>
/*...other routes...*/
<Route path="/home" component={Home} />
/*...other routes...*/
</Fragment> /* just a fragment wrapper around the routes for the user. This component is later rendered inside of my <Router> </Router>. This is done because the app has a lot of different routes. */
)
For some reason, this component keeps re-rendering over and over again, even if I do not interact with the application. I've checked to see if I am ever dispatching the same action from 2 places which could cause this issue, but I am not. I know I haven't given a lot to go on, but there is really not that much to show in this issue, I'm just hoping someone has encountered it before. I've dealt with infinite render loops before, they are usually obvious and easy to solve but this one is a bit odd.

I have solved my problem. I was actually using the old version of my RootRouter.js which had the Home component declared like so:
<Route path="/home" component={<Home user={this.props.user} />} />
And the Home component I was rendering was also an older version (it was a class component, not the function component I wrote in the question).
So here is what caused the issue:
the user prop is passed down when declaring the Home route, and it holds the value of state.user
Then in the Home component, I am using mapStateToProps and mapping the user to the props, and setting it to state.user.currentUser.
This causes the user prop to be declared once when rendering the component, then changing once the component is rendered, and then it will change again on the next render and so on. The reason why react did not respond to the render loop is because the user prop holds a different value for each render.

Related

connect a react hooks components that is not have a parent and child connection

i want to connect a react components, that doesn't have a parent and child connection, but i want to show and hide from the child of another components.
here it is the code :
import React, { useEffect } from "react";
import DashSidebar from "./DashSidebar";
import Catalog from "./components/catalog";
function Dashboard() {
useEffect(() => {
document.title = "Home";
}, []);
return (
<div id="l-dashboard">
<DashSidebar />
<Catalog />
</div>
);
}
export default Dashboard;
i have a button in DashSidebar and i want to show and hide Catalog
why i don't make it parent and child ?
because DashSidebar is a fixed position, and it is a side panel to the left, while i want to show and hide the content in the right side..
since you are using react-router this can be done with the below steps.
Wrap your Catalog list in the side bar with the Link component .
Now render the Catalog on the right with the help of Route .
Please refer the sandbox
React SideBar Example
Lift the state of the Catalog's visibility to the Dashboard component by passing a function in DashSidebar's props that changes a variable in Dashboard's state. Pass the variable in Catalog's props to control its visibility.

React component not reloading on history.push

I have an app with routes defined as follows:
<Route exact path="/:category/:product" component={ProductComponent} />
On category page, "/:category I have two buttons for each product, "Quick View" and "Full view", on click of which I want to move to product page, but also set the state for view like setView('quick') or setView('full').
I tried solving this by using onclick on buttons with the following code:
() => {
setView('quick')
history.push(`/${category.slug}/${product.slug}`)
}
here both are called, history api changes the url but component does not load for that url. I cannot use the react-router's <Link> component because I need to set the view before going to product page.
How can I use the history from react-router to perform page change like <Link> does?
Try this
Remove component and use render
<Route
exact
path="/:category/:product"
render={({ match }) => <ProductComponent key={match.params.product || 'empty'} />}
/>

React Native - Global screen outside React-Navigation

In my React Native application, I am using Redux to manage the global state of the app. This is what the render of my App.js looks like:
render() {
return (
<Provider store={store}>
<MainNavigator />
</Provider>
);
}
where <MainNavigator /> exports createAppContainer(createBottomTabNavigator({ ...my panel names })) and <Provider ... /> is imported from react-redux.
Everything works just fine. However, I would like to have a "global" screen which is independent of my navigator. I would like to have it mounted all the time immediately after the app has been launched and display it over the main navigator or hide it using Redux.
As an example, I am aiming to achieve something like the music screen on Spotify and SoundCloud which is always mounted while browsing through the whole app. Or as another example, the video screen on YouTube which serves the same purpose as the previous examples.
I have tried the following in my App.js:
render() {
return (
<Provider store={store}>
<GlobalScreen />
<MainNavigator />
</Provider>
);
}
However, this would split my application into two halves where the upper half of the screen displays the <GlobalScreen /> component and the lower half displays the <MainNavigator /> component.
How can I achieve a "global" screen, independent of React Navigation, which can overlay it or hide itself using Redux?
P.S. I am not sure if this can be achieved by having the screen outside React Navigation. But I am open to any suggestions which could help me achieve my desired goal.
You can use a conditional to render either the GlobalScreen or the MainNavigator.
{someConditionBasedOnDataFromStore ? (
<GlobalScreen/>
):(
<MainNavigator/>
)
You probably want this logic inside a component which connects to the store to get the data for your conditional.

how to access value of state of Root Component in the child component

I have created a React application. In this application, I've created following components:
App Component (root) : where data is loaded into state
CardList Component: List of Cards, data is passed to it using props
Card Component: use forEach to pass data to Card and it has button
CustomButton Component: acts like a button with style
What I want is when the user clicks button on any of the Card, a number should get increased everytime. But I am not able to access Data here.
Can anyone help?
Here is the Card Component for your reference:
class Card extends Component {
render() {
return (
<div className="tc bg-light-green dib br3 pa3 ma2 grow bw2 shadow-5">
<h2>Votes: {this.props.votes}</h2>
<div>
<img src={this.props.pic} alt='Superstar'/>
<div>
<h2>{this.props.name}</h2>
<p>{this.props.email}</p>
</div>
</div>
<ActionButton btnText="Vote Now!" clickhandler={this.onVoteButtonClick}/>
</div>
);
}
onVoteButtonClick = (event) => {
console.log('It was clicked : '+this.props.id);
}
}
Two options which you should research more to see what suits your need best:
Redux (or something similar)
The new Context API available in React 16.
The gist of either solution is that you're managing application state independently of the dependent component tree(s). The Context API is arguably easier to implement whereas you'll currently find many more examples explaining the Redux approach as it's still the most common solution right now.

How to force react component to re-render after deletion

I have a react component which renders a list of covers and a button to delete the book from the list onClick. I'm passing in the delete action via map dispatch to props and the bookshelf info via map state to props. The delete action is successfully deleting the book from the database and is hitting my reducer. But even though the state updates, the book list component does not re-render (the deleted book doesn't disappear unless I reload).
import React from 'react';
import { Link, withRouter } from 'react-router-dom';
import {CarouselProvider, Slider, Slide, ButtonBack, ButtonNext, Image, Dot} from 'pure-react-carousel';
class BookshelfIndexItem extends React.Component {
constructor(props){
super(props);
}
render() {
return(
<div>
<h3 className='shelf-title'>{this.props.bookshelf.name}</h3>
<CarouselProvider
naturalSlideWidth={135}
naturalSlideHeight={250}
totalSlides={this.props.bookshelf.book_ids.length}
visibleSlides={3}>
<Slider style={{height: '240px', display: 'flex', 'justifyContent': 'space-between'}}>
{this.props.bookshelf.book_ids.map((id, index) =>(<Slide index={index}
key={id} style={{'width': '150px','display': 'flex','alignItems': 'center', 'margin': '0 50px'}}>
<Link to={`/books/${id}`}>
<Image style={{height: '200px', width: '140px'}} src ={this.props.books[id].image_url}></Image>
</Link>
<button style={{height: '35px', 'marginLeft': '55px', 'marginTop': '5px'}}
onClick={() => this.props.deleteBookFromBookshelf({book_id: id, bookshelf_id: this.props.bookshelf.id})}>
X
</button>
</Slide>))}
</Slider>
<div className='slider-button-container'>
<ButtonBack className='slider-button'>back</ButtonBack>
<ButtonNext className='slider-button'>next</ButtonNext>
</div>
</CarouselProvider>
</div>
);
}
}
export default withRouter(BookshelfIndexItem);
I've tried a few of the lifecycle methods, such as fetching all the bookshelves in ComponentWillReceiveProps, but no change.
I have a strong suspicion I know what's up - any chance you're pushing to a copy of the array you're keeping in state and then setting state to that? When you push to an array (or add an item to a set or anything similar), you still end up with the same list object in memory (though with different contents). React is checking to see if there's a new list, and it sees the old list, sees the new list, and says "hey those are the same object in memory, so since they're the same I don't need to even trigger a shouldComponentUpdate.
I've produced a minimal example of this behavior so you can see it in action:
https://codesandbox.io/s/nrk07xx82j
If you hit the first button, you can see in the console that the new state is in fact correct, but the rendered list never updates (because of what I discussed above).
However, if you use array destructuring ([...list]), you end up returning the same array contents, but in a new array object, triggering a React update.
Hope that helps!
ninja edit: realizing I could be more specific since you're talking about deleting items - Array.pop and Array.splice() both behave in the same way, you're still dealing with the same object in memory and thus won't be triggering a component update.

Categories

Resources