Why Is Usestate Not Triggering Re-Render

Why is useState not triggering re-render?

You're calling setNumbers and passing it the array it already has. You've changed one of its values but it's still the same array, and I suspect React doesn't see any reason to re-render because state hasn't changed; the new array is the old array.

One easy way to avoid this is by spreading the array into a new array:

setNumbers([...old])

react useState not re rendering

Your useEffect is not being told to update. useEffect needs to be passed the value/dependencies that it needs to (trigger the) update on. Without it, the effect will only run once on (initial) component render

const [tagsWithData, setTagsWithData] = useState([]);
useEffect(() => {
....
const finalsTags = temp.map((item) => item.name);
setTagsWithData(finalsTags);
}, [temp]); // <--- add this

Below is a small example illustrating the differences. Click on the button, and check out the output of both effectWithDep and effectWithoutDep. You'll notice only effectWithDep will update.

// Get a hook function
const { useState, useEffect } = React;

const Example = ({title}) => {
const [count, setCount] = useState(0);

const [effectWithDep, setEffectWithDep] = useState(0);
const [effectWithoutDep, setEffectWithoutDep] = useState(0);

useEffect(() => {
setEffectWithDep(count)
}, [count])

useEffect(() => {
setEffectWithoutDep(count)
}, [])

return (
<div>
<p>{title}</p>

<p>effectWithDep: {effectWithDep}</p>

<p>effectWithoutDep: {effectWithoutDep}</p>

<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
};

// Render it
ReactDOM.render(
<Example title="Example using Hooks:" />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>

React useState is not re-rendering while it is referring cloned array by spreading

Sometimes I facing this issue too, in my case I just "force" render calling callback.

First solution:

const [reviews, setReviews] = useState([...book.reviews]);

useEffect(() => {
const sortedReviews = [...reviews]
.sort((review1: IReview, review2: IReview) =>
sortBy(review1, review2, sortRule));

setReviews(()=> [...sortedReviews]) // <-- Here
}, [sortRule])

second solution:
You can use useRef to get data in real time, see below:

    const [reviews, setReviews] = useState([...book.reviews]);
const reviewsRef = useRef();

useEffect(() => {
const sortedReviews = [...reviews]
.sort((review1: IReview, review2: IReview) =>
sortBy(review1, review2, sortRule));

setReviewRef([...sortedReviews])
}, [sortRule])

function setReviewRef(data){
setReview(data);
reviewsRef.current = data;
}

So, instead use the state reviews use reviewsRef.current as u array

I hope you can solve this!

useState not re-rendering component if it is called inside callback function

Update it with useRef,

The reason why useState doesnt show the latest value is because useState is asynchronous. So in your case useState is being updated without giving it a chance to re-render. In these cases useRef plays a better role.

import React, {useRef} from "react";

const Upload = () => {
const percent = useRef(0);

const progListener = (event) => {
percent.current = Math.round((event.loaded * 100) / event.total)
}

return (
<h6>{percent.current}</h6>
<Dropzone onDropAccepted={dropAccepted}/>
)
}

Why does React useState hook trigger a re-render before remaining code executed?

Doesn't setCount result in the scheduling of a re-render that is not immediate? Shouldn't the console log immediately after the setCount function call always execute before the re-render is triggered?

Under the hood, React optimizes re-renders by queuing and batching them when it can determine that it's safe to do so. When in a function React understands (such as a functional component, or a hook callback), if you call a state setter in one of those functions, React will know that it's safe to delay the state update until its processing is finished - for example, until all effect / memo / etc callbacks have run, and until all components from the original state have been painted onto the screen.

But when you call a state update outside of a built-in React function, React doesn't know enough about its behavior to know when it'll be able to re-render next if it delays the state update. The setTimeout call is not called from inside the React lifecycle, so batching updates and optimizing them is much more difficult - so, rather than React trying to guess how it could be done safely and asynchronously, it re-renders immediately.

Mocked useState not triggering a rerender React Testing Library

Why is the component not re-rendering when the value being returned by the useContext has been updated?

You mock React.useContext, its mock implementation breaks the real useContext feature. This means React doesn't know how to detect the context changes because you replace useContext with your mock.

Is there just a better way to achieve this. I feel like I might be going all around the world to do something that is probably simple

Don't mock anything provided by React, incorrect mock will break the feature of React. If your test case is built on these incorrect mocks, your test cases may pass but your code may be wrong in runtime. For more info, see What you should avoid with Testing Library

Render the context provider and component as usual, use RTL queries to find the target element, and fire an event on it. Then check what has been rendered of the component.

E.g.

TestContext.tsx:

import React, { createContext, Dispatch, SetStateAction, useState } from 'react';

export const TestContext = createContext<{
stateVariable: number;
setStateVariable: Dispatch<SetStateAction<number>>;
}>(null!);

export default function TestContextProvider({ children }) {
const [stateVariable, setStateVariable] = useState(1);
return <TestContext.Provider value={{ stateVariable, setStateVariable }}>{children}</TestContext.Provider>;
}

Component.tsx:

import React from 'react';
import { useContext } from 'react';
import { TestContext } from './TestContext';

export default function ComponentToBeTested() {
const { stateVariable, setStateVariable } = useContext(TestContext)
return (
<>
<h1>
Current value: <span data-testid="answer">{stateVariable}</span>
</h1>
<button data-testid="increment-button" onClick={() => setStateVariable(stateVariable + 1)}>
Increment
</button>
</>
);
}

Component.test.tsx:

import React from 'react';
import { fireEvent, render, screen } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import ComponentToBeTested from './Component';
import TestContextProvider from './TestContext';

describe('73039704', () => {
test('should pass', () => {
render(
<TestContextProvider>
<ComponentToBeTested />
</TestContextProvider>
);
expect(screen.getByTestId('answer')).toHaveTextContent('1');
const button = screen.getByTestId('increment-button');
fireEvent.click(button);
expect(screen.getByTestId('answer')).toHaveTextContent('2');
});
});

Test result:

 PASS  stackoverflow/73039704/Component.test.tsx (11.423 s)
73039704
✓ should pass (33 ms)

-----------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-----------------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
Component.tsx | 100 | 100 | 100 | 100 |
TestContext.tsx | 100 | 100 | 100 | 100 |
-----------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 12.116 s

React not triggering re-render with useState hook

Ok, I fixed this by updating the object through the cloneAndUpdate (Hooks API Reference) method instead of afterwards

assign[personInfoTypeId] = {$push:[newEmptyRow]};
let newPersonInfoTypeFieldsState = cloneAndUpdate(formValuesById,assign);
setFormValues(newFormValuesState);

I think the library isn't actually doing a deep copy but re-using parts of the structure that haven't changed so I suspect trying to manually update this afterwards was actually updating the original reference that react new about and thus react recognised no difference when setting state. Using the correct $push syntax documented in the library fixed it.

After changing UseState Hook React component not re-rendering

In useToken.js

  const saveToken = userToken => {
localStorage.setItem('token', JSON.stringify(userToken));
setToken(userToken.token);
}

should be replaced to

  const saveToken = userToken => {
localStorage.setItem('token', JSON.stringify(userToken));
setToken(userToken); //userToken.token changed to userToken
}

Because, there is no variable named as 'token' in userToken object. It is token itself. So the hook thinks that there is no change and because of that it does not re-render.

Initial values in useState not updated after re-render

The selectedTags state in the useState hook is only initialized once.

If tags remains a truthy defined array (even empty arrays are truthy and defined) then the EditTagsModal component remains mounted by {tags && <EditTagsModal existingTags={tags} />}. If the tags value, and thus the existingTags prop updates and EditTagsModal is rerendered, then you should implement an useEffect hook with a dependency on the existingTags prop to update the local state when the prop value updates.

useEffect(() => {
setSelectedTags(existingTags);
}, [existingTags]);

useEffect with a dependency array is synonymous to a class component's componentDidMount and componentDidUpdate lifecycle methods.



Related Topics



Leave a reply



Submit