I have a state in redux that tells me the current number of posts that are rendered on the page
it looks like this
{
postReducer:{
...state,
loaded:0,
}
}
I have multiple components that should use this state when I switch between them.
Example:
I am on the home page all the posts from people I follow are there the state looks like this
{
postReducer:{
...state,
loaded:15,
posts: [],
profilePosts: [],
}
}
when I go from home to my profile or any other persons profile I want to see their posts only
but in order to start fetching their posts, the loaded state must be 0 but it's the same 15
I want the loaded to be 0 every time I go to a new component and keep its value while I am on the same component
I am using react-router-dom to switch between pages
I tried resetting it in the useEffect but the first batch of posts are the ones that are loaded only
I am not sure, what is the issue with useEffect.
I believe that you can do this in two ways:
Once your component loaded you can set 0 to loaded posts number:
const dispatch = useDispatch();
useEffect(() => {dispatch(cleanupPostsNumberAction)}, []);
Once your component unloaded (when you move from one page to another), you can use cleanup function to set loaded to 0
const dispatch = useDispatch();
useEffect(() => () => {dispatch(cleanupPostsNumberAction)}, []);
So if this answer is not complete, you can provide a little bit more context on the problem you have
Related
After building the homepage of my website I finally figured out how to dynamically navigate to other pages. I wanted the browser to render the State homepage when a user clicked on a dropdown and selected a state. The navigation works, but it re-renders the component 50 times which I do not understand. I suspect it is due to the map function that is creating the menuitems. I could build out 50 individual menuitems but that is really ugly.
I am just starting out learning React. I have 7 YRS experience in backend development, but I am still trying to get a handle on React development. I have created a wepage with Material UI that has a dropdown that looks like this
<FormControl>
<InputLabel>Select a State</InputLabel>
<Select value={location} onChange={selectionChangeHandler}>
{locations.map((value) => (
<MenuItem value={value.toLowerCase()} key={value.toLowerCase()} component={Link} to={`${value.toLowerCase()}/home`} >
{value}
</MenuItem>
))}
</Select>
</FormControl>
This returns a dropdown with the 50 states in it. When the user clicks on a state I want the program to route to that page on click. This dynamic routing works BUT. It re-renders my component 50 times. I think this is happening because the dropdown is being built inside of a .map functions and there are 50 entries in that list.
I can remove the map function and hardcode in 50 menuitems but that is ugly.
Here is my onChange Function
const selectionChangeHandler = (event) => {
console.log(event.target.value)
}
I have also tried removing the component={Link} and using the useNavigate hook in the selectionChangeHandler like so
const selectionChangeHandler = (event) => {
console.log(event.target.value)
setlocation(event.target.value)
link = `${event.target.value}/home`
navigate(link)
}
This works but also renders 50 times. I am at a loss.
I cross posted the above to reddit and then I researched a little more. It turns out in React. When a parent component's state is updated it re-renders all child components. This may be what is going on here, but I do not know how to fix it. Even if I pass the state as a prop to the child component I still have to link to the correct page.
I am kind of nervous about posting I really tried to put work into solving my own problem before reaching out for help, and I might be reaching out for help a lot as I learn. I am committed to learning, but some problems I just cannot figure out on my own.
Link to Code Link to Code
The problem here is inside StateHome.js. You use a naked axios.get call directly in your component so it's going to re-render anytime the component changes from state change.
When you get the results you set state inside the same component which re-renders the component, which then re-runs the axios.get call, causing the loop.
I understand you want to call that endpoint when someone lands on the /alabama/home page. To tell React "Do this one time when the component loads" you must use a useEffect with an empty dependency array. So instead of this:
const StateHome = () => {
const { location } = useParams();
const [PageData, SetPageData] = useState();
axios.get(`http://localhost:4000/${location}/home`).then((response) => {
console.log(response.data);
console.log(response.status);
console.log(response.statusText);
console.log(response.headers);
console.log(response.config);
SetPageData(response.data);
});
return <h1>This is my State Home Page Component for State {location}</h1>;
};
You need to use this:
const StateHome = () => {
console.log("StateHome");
const { location } = useParams();
const [PageData, SetPageData] = useState();
useEffect(() => {
axios.get(`http://localhost:4000/${location}/home`).then((response) => {
console.log(response.data);
console.log(response.status);
console.log(response.statusText);
console.log(response.headers);
console.log(response.config);
SetPageData(response.data);
});
}, []); // <--- indicates "on mount", loads once regadless of other side-effects (setState)
return <h1>This is my State Home Page Component for State {location}</h1>;
};
One of the journeys in my app kicks off several consecutive dispatches, one main dispatch with several side effects based on the result of the preceding one - with each API call making a Redux state change via the reducer.
After the API calls I am feeding data to a separate microservice to bring back logic that will dictate the sub-component to render. And it is in these sub-components that I am wanting to make a single datalayer push.
The issue I am having is that I am getting multiple renders/rerenders due to the constant data changes each time the reducer is hit, as you would imagine... And each time the main parent component is rendered/rerendered due to state change, I am sending a datalayer push as the sub-component is rendered again...
I'm wondering if there are any ways in which I can stop the rendering so much and the constant triggering of my sub-component and its datalayer push.
Note - I have tried wrapping these components with React.memo and using a custom prop checker using lodash.isEqual, however the Redux state changes slightly after each reducer call, so this doesn't really help.
SubComponent.jsx
const SubComponent = props => {
useEffect(() => {
// Do datalayer push here
}, []); // useEffect runs once on render
// Return html here
}
MyComponent.jsx
const mapStateToProps = state => ({...});
const mapDispatchToProps = dispatch => ({...});
const MyComponent = (props) => {
// Note: Runs on each render - Will be required to run when any redux state changes
useEffect(() => {
// Set up microservice wizard config
});
return (
<div>
{microserviceWizard.renderPage(props.step)}
<EcommerceHandler /> // This also makes Redux state changes
</div>
);
}
So I have this screen that show product details, it works like a template because only the data coming navigation params changes, I am having the issue because there's no reload, it works fine when going back and mounting it again, that's not the case here since I have related products showing on that same screen, I'll need a way to be able to either reload the current route or update the state
I have checked with console.log nothing shows up on second click
constructor (props) {
super(props)
this.state = {
product: this.props.route.params.product,
id: this.props.route.params.product.id,
}
}
To navigate to use to the route I use on both the screen itself or in another route
viewProduct = (product) => {
this.props.navigation.navigate('SingleProduct', { product: product })
}
I have tried setState inside of both componentDidMount and UNSAFE_componentWillReceiveProps but the results only shows after an additional click
You can use the following function to navigate from singleProduction screen to the same screen with the different params.
this.props.navigation.replace('SingleProduct', { product: product })
use the route.params?.product directly and mention change in useEffect array.
useEffect(() => {
return () => {
// Clean up the subscription
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [route.params?.product]);
This project is using NextJS
I have a page which URL looks like this
localhost:3000/first/second
I call getInitialProps on that page. Data is passed from getInitialProps to the component as it should be and I am setting initial state with that data like this
const Index = ({ data }) => {
const [state, setState] = useState(data.results)
}
Now when I send the user to
localhost:3000/first/third
getInitialprops is called as it should be. data, that is a parameter of component, is always populated with the latest results from getInitialProps but state isn't. It is populated with previous data that was in it. It is not resetting when I am doing client-side routing. Only when I hard refresh the page.
As suggested on NextJS GitHub I tired adding data.key = data.id, in ```getInitialProps``,` that is always a different number to force state update but it is not working.
Any info will be useful.
Thank you for your time :)
When using NextJS, getInitialProps is called before the page renders only for the first time. On subsequent page renders (client side routing), NextJS executes it on the client, but this means that data is not available before page render.
From the docs:
For the initial page load, getInitialProps will run on the server only. getInitialProps will then run on the client when navigating to a different route via the next/link component or by using next/router.
You would require a useEffect to sync state:
const Index = ({ data }) => {
const [state, setState] = useState(data.results);
useEffect(() => {
setState(data.results);
}, [data]);
}
Using sockets to listen to the server; the Redux store continually updates with thousands of records of data. The updating of the store only takes a couple of seconds with thousands of objects getting dispatched through actions. However, using the redux connect function to map state to my component with mapStateToProps seems to queue up the changes to the state and updates the state of the component at around 7-10 records per second. This means the React Component takes a really long time to render. Are there any solutions to speed this up? Also, the exact amount of data will always be changing and there is no fixed amount.
Here is my component:
class TestComponent extends Component {
state = {};
componentDidMount() {
this.props.connectToSocket();
}
render() {
const { classes, width, people, vehicles, incidents, locations } = this.props;
return (
<div>
Hello
</div>
);
}
}
TestComponent.propTypes = {
classes: PropTypes.object.isRequired
};
const mapStateToProps = state => {
console.log(state);
return {
people: state.live.people,
vehicles: state.live.vehicles,
incidents: state.live.incidents,
locations: state.live.locations
}
};
const mapDispatchToProps = {
connectToSocket: connectToSocket
};
export default connect(mapStateToProps,mapDispatchToProps(TestComponent));
The action that initialises the socket is executed in the componentDidMount() function. I can then see the state being printed in the console, however, it prints every update with around 7-10 new records a second. With over 5000 updates to the store occurring in a very short time span, mapping the redux store to the props of the component takes a much longer time and it takes over 5 minutes to render the component.
Any ideas?
Generally, the answers here involve some form of batching:
You could batch up the data coming from the socket, so that instead of dispatching N actions with 1 value apiece, you maybe dispatch 5 actions with N/5 values each, or something along that line.
You can use one of the various batching middleware or store enhancers to cut down on the number of Redux subscription notifications.
See the Redux FAQ entry on reducing the number of store update events for further ideas and links.