How to reload a react component without using any event handler? - javascript

Is it possible to reload a component in react without using the help of a function like onClick. I want my component to reload again after it just got loaded.
I tried using
window.location.reload(false); in the constructor. I even tried using useEffect() but they make the page reload infinitely.
Is there any work around?

I have seen people accomplish this by setting a dummy setState method that is strictly used to trigger a refresh. Consider this component:
export const Proof = () => {
console.log("Component re-rendered")
const [dummyState,rerender] = React.useState(1);
const onClick = () => {
rerender(dummyState + 1);
}
React.useEffect( () => {
console.log("dummyState's state has updated to: " + dummyState)
}, [dummyState])
return(
<div>
<button onClick={onClick}>reRender</button>
</div>
)
}
Whenever you want to rerender the component you just need to trigger a change in the dummyState. Clicking the button will cause the components state to change, which will cause the component to rerender (i used a console.log() for proof). It is worth noting that simply calling a method that instantly changes the state of the component will result in an infinite loop.
From reading your comment above, it sounds like you are passing a value as a prop to a child component and you would like the value to be recalculated as soon as any interaction with that component occurs. Ideally, the interaction itself should cause the recalculation. But if you would just like to quickly recalculate the value and rerender the component as soon as it renders then i think this would work:
export const Proof = () => {
console.log("Component re-rendered")
const [dummyState,rerender] = React.useState(1);
//the empty brackets will cause this useEffect
//statement to only execute once.
React.useEffect( () => {
rerender(dummyState + 1);
}, [])
return(
<div>
<p>dummyState</p>
</div>
)
}
You could also recalculate the value in the useEffect method, as it will get called as soon as the component initially renders but not on any subsequent rerenders (due to the empty brackets as the second parameter to the useEffect method)

Have you tried to enter some dependecies to your useEffect hook ?
Like :
useEffect(() => {
console.log('hello');
}, [isLoad];
Here you're component will re-render only if isLoad is changing.

Related

setting state with useState affects all useState hooks

I am trying to build an ecommerce website, and I hit a problem I cannot seem to resolve. I am very new to react and JS so have some patience please :)
I declared 4 useStates in my app.js:
const [elementeDinState, setElementeDinState] = useState([]);
const [currentCategorie, setCurrentCategorie] = useState("Acasa");
const [subCategorie, setSubcategorie] = useState([]);
const [cartContents, setCartContents] = useState([]);
const fetchData = useCallback(async () => {
const data = await getCategories();
setElementeDinState(data);
}, []);
useEffect(() => {
fetchData().catch(console.error);
}, [fetchData]);
const changeHeader = (dataFromMenuItem) => {
setCurrentCategorie(dataFromMenuItem);
};
const changeCopiiContent = (data1FromThere) => {
setSubcategorie(data1FromThere);
};
const changeCart = (dataFromCart) => {
setCartContents(dataFromCart);
};
I am passing the functions to change those states to different child components as props. my problem is, when I add items to cart it triggers a re render of my component (products listing component) that should not be affected by cartContents and that resets the state of said component to the initial value that changes the items being shown. does useState hook create a single global state comprised of all those states?
If these useState are defined in the app.js and then passed down, when a child will use them chasing the state will happen in the app.js so all the children of <App /> will be re-rendered.
I guess that your app.js looks similar:
function App() {
const [elementeDinState, setElementeDinState] = useState([]);
// ...and the other hooks and methods
return (
<cartContents setElementDinState={setElementeDinState} />
<ProductList />
)
}
In this case the state is in the component so when <CartContents /> changes it, it will trigger a re-render of the and all its children <ProductList /> included.
To avoid this problem think better when each piece of state needs to be and put the state as near as possibile to that component. For example, if the state of the cart does not influence the Product list. Move the useState in the <Cart /> component.
From what I understand, your problem is that you're simply resetting the cartContents state every time you call the changeCart function, correct?
What you probably want, is to add (or remove ?) the item to the cart, like this?
const changeCart = (dataFromCart) => {
setCartContents(oldContents => [...oldContents, dataFromCart]);
};
Here is a description of useState from the oficial site:
useState is a Hook (...). We call it inside a function component to add some local state to it
So it creates just a local state.
About your problem, We need more information, but I believe that some parent component of that widget is trying to render other component instead of your the component that you wanted (let's call it "ProblemComponent") and rendering you ProblemComponent from scratch again, before you can see it.
it's something like that:
function ParentComponent(props: any) {
const isLoading = useState(false);
// Some logic...
if(isLoading) {
return <LoadingComponent/>;
}
return <ProblemComponent/>;
}
If that doesn't work you can also try to use React.memo() to prevent the ProblemComponent to update when it props change.
well, seems like I wanted to change the way react works so I figured out a work around, based on what you guys told me. I declared the state of the productsComponent in the parent component and adding to cart now doesn't force a refresh of the items being shown. thank you!

Calling function directly in functional component body in React.js

I have a functional component and some function is called directly in the body (that function changes the state && it's needed to be run each render). Is it 100% okay, or could it cause bugs?
const myFunc = (setMyState, ...) => {
...
setMyState(...)
}
const MyComponent = () => {
const [myState, setMyState] = useState(...)
myFunc(setMyState, ...)
return(<div>some content...</div>)
}
This will cause an infinite loop. This is why:
component renders first time
function updates component state
component renders because of state update
function runs again and updates component again
etc
You can definitely avoid this by using if-statements inside the function, but the best way to update a component state is either by user input/api calls or props updating(your case I suppose)
The best way to do that in my opinion is using the useEffect hook

need to pass an effect via props or force component reload from outside the root component in preact/react

I have a situation that an item outside the component might influence the item in the backend. ex: change the value of one of the properties that are persisted, let's say the item status moves from Pending to Completed.
I know when it happens but since it is outside of a component I need to tell to the component that it is out of sync and re-fetch the data. But from outside. I know you can pass props calling the render method again. But the problem is I have a reducer and the state will pick up the last state and if I use an prop to trigger an effect I get into a loop.
Here is what I did:
useEffect(() => {
if (props.effect && !state.effect) { //this runs when the prop changes
return dispatch({ type: props.effect, });
}
if (state.effect) { // but then I get here and then back up and so on
return ModelEffect[state.effect](state?.data?.item)}, [state.status, state.effect, props.effect,]);
In short since I can't get rid of the prop the I get the first one then the second and so on in an infinite loop.
I render the root component passing the id:
render(html`<${Panel} id=${id}/>`,
document.getElementById('Panel'));
Then the idea was that I could do this to sync it:
render(html`<${Panel} id=${id} effect="RELOAD"/>`,
document.getElementById('Panel'));
any better ways to solve this?
Thanks a lot
I resolved it by passing the initialized dispatch function to a global.
function Panel (props) {
//...
const [state, dispatch,] = useReducer(RequestReducer, initialData);
//...
useEffect(() => {
//...
window.GlobalDispatch = dispatch;
//...
}, [state.status, state.effect,]);
with that I can do:
window.GlobalDispatch({type:'RELOAD'});

Changing React state happens a re-render later, then it should

I have a functional component managing several states, amongst other things a state, which stores the index, with which I am rendering a type of table, when clicking a suitable button. OnClick that button calls a callback function, which runs a click handler. That click handler changes the index state, to the same 'index' as the array entry, in which I store an object with information for the rendering of a child component.
I would expect, that onClick the state would change before the rendering happens, so the component could render correctly. Yet it only happens a render later.
I already tried calling the useEffect-hook, to re-render, when that index state changes, but that didn't help neither.
Here is a shortened version of the code:
export const someComponent = () => {
[index, setIndex] = useState(-1);
const handleClick = (id) => {
setIndex(id);
// This is a function, I use to render the table
buildNewComponent(index);
}
}
Further 'down' in the code, I got the function, which is rendering the table entries. There I pass the onClick prop in the child component of the table as following:
<SomeEntryComponent
onClick={() => handleClick(arrayEntry.id)}
>
// some code which is not affecting my problem
</SomeEntryComponent>
So as told: when that onClick fires, it first renders the component when one presses it the second time, because first then the state changes.
Could anyone tell me why that happens like that and how I could fix it to work properly?
As other have stated, it is a synchronicity issue, where index is being updated after buildComponent has been invoked.
But from a design standpoint, it would be better to assert index existence by its value, as opposed to flagging it in a handler. I don't know the details behind buildComponent, but you can turn it into a conditional render of the component.
Your component rendering becomes derived from its data, as opposed to manual creation.
export const someComponent = () => {
[index, setIndex] = useState(-1);
const handleClick = (id) => setIndex(id);
const indexHasBeenSelected = index !== -1
return (
<div>
{indexHasBeenSelected && <NewComponent index={index} />}
</div>
)
}
When calling buildNewComponent the index is not yet updated. You just called setState, there is no guarantee that the value is updated immediately after that. You could use id here or call buildNewComponent within a useEffect that has index as its dependency.
I believe that you can have the correct behavior if you use useEffect and monitor index changes.
export const someComponent = () => {
[index, setIndex] = useState(-1);
useEffect(() => {
// This is a function, I use to render the table
buildNewComponent(index);
}, [buildNewComponent, index])
const handleClick = (id) => {
setIndex(id);
}
}
The process will be:
When the use clicks and call handleClick it will dispatch setIndex with a new id
The index will change to the same id
The useEffect will see the change in index and will call buildNewComponent.
One important thing is to wrap buildNewComponent with useCallback to avoid unexpected behavior.

componentDidUpdate not firing after the first time the component is mounted (React)

I have a function that switches windows (components) on a single page by clicking a button.
The function that swaps windows:
getProfileContent = () => {
var html = [];
if (this.state.content === "picks") {
html.push(<Picks picks={this.state.picks} deletePick={this.deletePick} />);
}
if (this.state.content === "api") {
html.push(<Admin admin="admin" />);
}
if (this.state.content === 'settings') {
html.push(<Settings />);
}
return html;
};
The content defaults to "picks" when the parent component initially loads and the first time the "Picks" component loads everything works fine because the componentDidUpdate update function below is triggered:
"Picks" component update function:
componentDidUpdate(prevProps, prevState) {
console.log('here')
if (Object.keys(prevProps.picks).length !== Object.keys(this.props.picks).length) {
this.sortPicks();
}
}
However, after swapping windows via getProfileContent and coming back to the "Picks" component the componentDidUpdate function is not triggered. I have also tried adding a different "key" value to the Picks component in hopes the new prop would trigger the componentDidUpdate, but no luck. I have the console log outside if the condition so I know componentDidUpdate isn't being called regardless of the condition. Any help is appreciated, thanks!
This question has been solved with help from #lanxion. Basically, the first time the component mounts the componentDidUpdate function is called, but only because of the parent component updating and passing in new props. The second time the component is mounted the parent already has the correct props, thus only componentDidMount is called and not componentDidUpdate. Placing the code in componentDidMount and componentDidUpdate (with conditionals) solved my issue,
It is possible that the props values are not changing, thus the same props values are being passed down and thus have the same length. Quite possibly it IS reaching the componentDidUpdate() hook, but the condition returns false and thus you don't go into the sortPicks() function. Hard to say without knowing the rest of the code.

Categories