Is automatic update of React's context usual behavior? - reactjs

I thought that context acts in a similar way as state so I've created function handleUpdate that can update state which is context using as a value. Afterwards I've noticed that context is being updated without triggering handleUpdate.
Provider:
<DashboardContext.Provider value={{dashboard:this.state,handleChange:this.handleChange}}>
{/*...*/}
</DashboardContext.Provider>
handleChange function
handleChange=(what, value)=> this.setState({[what]:value});
In another component which uses context: this triggers updating of context without calling handleUpdate.
let updatedTasks = this.context.dashboard.tasks;
updatedTasks[task.id] = {/* ... something */};
Afterwards it changes context value and parents state (which is context using) without calling setState. Is this usual behavior? I though that all states should be handled with setState function.
As the actual workaround to lose reference on contexts object I've used:
let updatedTasks = JSON.parse(JSON.stringify(this.context.dashboard.tasks));
but it doesn't seems like a correct solution for me.
Edit: as #Nicholas Tower suggested solution:
my current code
State in constructor now looks like this:
this.state = {
value: {
dashboard: {
// everything from state is now here
},
handleChange: this.handleChange,
}
};
I pass state.value instead of custom object now
<DashboardContext.Provider value={this.state.value}>
{/*...*/}
</DashboardContext.Provider>
but still when I do this, context and state (both) are being updated without calling handleChange
let updatedTasks = this.context.dashboard.tasks;
updatedTasks[task.id] = {/* ... something */};

The issue you have is in this part:
value={{dashboard:this.state,handleChange:this.handleChange}}
Every time your component renders (for whatever reason), this line will create a new object. The dashboard property and handleChange property may be unchanged, but the object surrounding them is always new. That's enough that every time it renders, the value changes, and so all descendants that use the value need to be rerendered too.
You'll need to modify your code so that this object reference does not change unless you want it to. This is typically done by putting the object into the component's state.
class Example {
handleChange = (what, value) => {
this.setState(oldState => ({
value: {
... oldState.value,
[what]:value
}
});
}
state = {
value: {
dashboard: {
// whatever you used to put in state now goes here
},
handleChange: this.handleChange
}
}
render() {
return (
<DashboardContext.Provider value={this.state.value}>
{/*...*/}
</DashboardContext.Provider>
)
}
}
You can see mention of this in React's documentation on context: https://reactjs.org/docs/context.html#caveats

Related

useEffect has missind dependencies when I want to run it always just once on mount

I have a component that's supposed to read a property from the component (which is either string "fill" or string "stroke") and pull the according key from an object to read it's context.
This gets mounted as soon as an object is selected, accesses the active object and pulls out it's color either as a fill color or a stroke color.
useEffect(() => {
setButtonColor(context.objects.getActiveObject()[props.type]);
}, []); //on mount
Mounting it like this:
<ColorPicker type="fill" />
<ColorPicker type="stroke" />
This supposed to run only once on mount. I thought when the dep array is empty, it runs on any case once when it's mounted.
So how do I run something once on mount utilizing props and context?
And why does it need a dependency at all when I want it to ALWAYS run only ONCE on mount, no matter what?
It's best to move away from the thinking that effects run at certain points in the lifecycle of a component. While that is true, a model that might help you better get to grips with hooks is that the dependency array is a list of things that the effect synchronizes with: That is, the effect should be run each time those things change.
When you get a linter error indicating your dependency array is missing props, what the linter is trying to tell you is that your effect (or callback, or memoization function) rely on values that are not stable. It does this because more often than not, this is a mistake. Consider the following:
function C({ onSignedOut }) {
const onSubmit = React.useCallback(() => {
const response = await fetch('/api/session', { method: 'DELETE' })
if (response.ok) {
onSignedOut()
}
}, [])
return <form onSubmit={onSubmit}>
<button type="submit">Sign Out</button>
</form>
}
The linter will issue a warning for the dependency array in onSubmit because onSubmit depends on the value of onSignedOut. If you were to leave this code as-is, then onSubmit will only be created once with the first value of onSignedOut. If the onSignedOut prop changes, onSubmit won't reflect this change, and you'll end up with a stale reference to onSignedOut. This is best demonstrated here:
import { render } from "#testing-library/react"
it("should respond to onSignedOut changes correctly", () => {
const onSignedOut1 = () => console.log("Hello, 1!")
const onSignedOut2 = () => console.log("Hello, 2!")
const { getByText, rerender } = render(<C onSignedOut={onSignedOut1} />)
getByText("Sign Out").click()
// stdout: Hello, 1!
rerender(<C onSignedOut={onSignedOut2} />)
getByText("Sign Out").click()
// stdout: Hello, 1!
})
The console.log() statement does not update. For this specific example that would probably violate your expectations as a consumer of the component.
Let's take a look at your code now.
As you can see, this warning is essentially stating that your code might not be doing what you think it is doing. The easiest way to dismiss the warning if you're sure you know what you're doing is to disable the warning for that specific line.
useEffect(() => {
setButtonColor(context.objects.getActiveObject()[props.type]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []); //on mount
The correct way to do this would be to place your dependencies inside of the array.
const { type } = props
useEffect(() => {
setButtonColor(context.objects.getActiveObject()[type]);
}, [context, type]);
This would, however, change the button colour every time type changed. There's something to note here: You're setting state in response to props changing. That's called derived state.
You only want that state to be set on the initial mount. Since you only want to set this on the initial mount, you could simply pass your value to React.useState(initialState), which would accomplish exactly what you want:
function C({ type }) {
const initialColor = context.objects.getActiveObject()[type];
const [color, setButtonColor] = React.useState(initialColor);
...
}
This still leaves the problem that the consumer might be confused as to why the view does update when you change the props. The convention that was common before functional components took off (and one I still use) is to prefix props that are not monitored for changes with the word initial:
function C({ initialType }) {
const initialColor = context.objects.getActiveObject()[initialType];
const [color, setButtonColor] = React.useState(initialColor);
}
You should still be careful here, though: It does mean that, for the lifetime of C, it will only ever read from context or initialType once. What if the value of the context changes? You might end up with stale data inside of <C />. That might be acceptable to you, but it's worth calling out.
React.useRef() is indeed a good solution to stabilize values by only capturing the initial version of it, but it's not necessary for this use-case.
This is my workaround for the issue:
Set the color to a variable and then use that variable to set the button color on mount of the component.
const oldColor = useRef(context.objects.getActiveObject()[props.type]);
useEffect(() => {
setButtonColor(oldColor.current);
}, []); //on mount
useRef returns a mutable ref object whose .current property is
initialized to the passed argument (initialValue). The returned object
will persist for the full lifetime of the component.

Where is the best place to make calculations outside the render method in React?

I have a render method in my container component like this:
render() {
const { validationErrors } = this.state
const { errorsText, errorsFields} = validationErrors.reduce(
(acc, error) => {
acc.errorsText.push(error.text)
acc.errorsFields[error.field.toLowerCase()] = true
return acc
},
{
errorsText: [],
errorsFields: {},
},
)
return (
<MyViewComponent
errorsText={errorsText}
errorsFields={errorsFields}
/>
)
}
As you can see every render there are some computations happens (returned array and object with the new values), then I pass it into my child component as a props. I have a feeling that this is a wrong pattern. We should keep render function 'pure'. Isn't it? The question is: Where is the best place for making such computations outside the render?
If this were a functional component (which I highly recommend you use in the future, by the way), you'd be able to use the 'hook' useEffect to recalculate errorsText and errorsField whenever this.state.validationErrors changes, and only when it changes.
For your Class Component, however, I assume at some point you set this.state.validationErrors. What you should do is create a method that runs your reducer and stores errorsText and errorsField to state, then place a call to this method after each point you set this.state.validationErrors. Then, remove the logic in the render method and replace errorsText and errorsField with this.state.errorsText and this.state.errorsField respectively.
Doing this will ensure you only ever run your reducer when necessary (i.e. when this.state.validationErrors changes).
Your component would end up looking something like this:
class MyComponent extends Component {
...
someCallback() {
const validationErrors = someFunctionThatReturnsErrors();
// We do the logic here, because we know that validationErrors
// could have changed value
const { errorsText, errorsFields } = validationErrors.reduce(
(acc, error) => {
acc.errorsText.push(error.text);
acc.errorsFields[error.field.toLowerCase()] = true;
return acc;
}, {
errorsText: [],
errorsFields: {},
},
);
// Put everything in the state
this.setState({
validationErrors, // you may not even need to set this if it's not used elsewhere`
errorsText,
errorsFields
});
}
...
render() {
const {
errorsText,
errorsFields
} = this.state;
return (
<MyViewComponent
errorsText={errorsText}
errorsFields={errorsFields}
/>
);
}
}
It is pure, as it has no side effects.
As long as this does not create performance issues I see no problem with this. If it does create performance issues, you should look into memoizing the reduce. If you were using hooks you could use the built-in React.useMemo for this. While using class version you could look into something like https://www.npmjs.com/package/memoize-one

React-redux add a new variable to state

I am struggling to get working on a React-redux application to pass a value via the state from one component on to another. I do not wish to use the reducer and/or dispatch because I am not calling a webservice, I just want to take a value from a textbox form to enable another control on the order component.
Firstly, I can grab the value from control, but when mapstatetoprops is called the variable I set and wish to add to the state is undefined. This also possibly explains why my other problem. On my other component the function to use props is never called because of the state-componentWillReceiveProps
Here is the relevant code snippet :
<ListItemContent>
<Control component={Textfield} model="somemodel" label="MyLabel" onBlur={this.onChangeOfValue}/>
</ListItemContent>
onChangeOfValue = (event) => {
this.setState({
newValueToPassAlong: event.target.value
}); //newValueToPassAlong is set in constructor
};
let mapStateToProps = (state) => {
return {
newValueToGive: state.newValueToPassAlong
} // This is undefined
};
export default connect(mapStateToProps)(form)
So, my question is how do add a new variable to a state using React-redux without the need of reducers, etc and with this can I access this variable in my other component?
Ok action creator preferences aside, if you want to sync something between two components which share a common parent, you have to bring that state into the parent.
class ParentComponent {
onItemChanged = (newData) => {
this.setState({ thing: newData });
}
render() {
return (
<div>
<ChildA onItemChanged={ this.onItemChanged } />
<ChildB thing={ this.state.thing } />
</div>
);
}
}
when you change the value in ChildA, use this.props.onItemChanged with the new value. In ChildB, that value will be synchronised in this.props.thing. React will handle all the Component/prop updates.
That all being said, I genuinely think there's nothing wrong with using an action creator/reducer.
setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value.
Refer documentation for more about setState()

Accessing and setting state in an react/flux application

I'm working on a react app with a flux implementation.
I have a store which is bound to a component and in the ctor I set some default (blank) state values. In componentWillMount I populate the state by firing some actions which update the store data.
The store emits a change and the component handles that change by putting bits of the store data into state.
In my render method, I'm wanting the render to depend on the state data.
At the moment I have a couple of issues.
If in my render method I do something like this.state.MyThing.AProperty then the render method is called too early when MyThing hasn't been populated yet. This seems to occur in a lot of places where I want a render to use state data. Is there a sensible guard against this or am I doing this wrong?
I'm using a store to emit a change, and handling that change by getting data from the store and setting it to the state of the component. My thinking here is that if I set it as state then the component will know to re-render when the state changes. Is this correct? or should I be getting the data from the store in the emit handler and using it directly? or setting it to a local var in the component?
The reason I ask is that I seem to encounter issues with setState calls not being immediate and wanting to use state as soon as I set it. With this in mind it seems like I might be doing it wrong.
Any thoughts greatly appreciated.
If you use conditionals in your render, then you can guard against unpopulated data being rendered.
<div>
{typeof this.state.myThing == 'object' ?
<strong>this.state.myThing.aProperty</strong> :
<span>Nothing to see here</span>}
</div>
And with regards to your second question, yeah. That's totally fine and it's the expected way to work with Flux. You can even take inspiration from Redux & Co and make higher order components that map store state to props.
function connect(store, mapStateToProps, Component) {
return React.createClass({
getInitialState() {
const state = store.getState();
return { state };
},
componentWillMount() {
store.listen(state => this.setState({ state }));
},
render() {
const stateProps = mapStateToProps(this.state);
const passedProps = this.props;
const props = Object.assign({}, stateProps, passedProps);
return <Component {...props} />;
}
});
}
This pattern allows you to take an existing component and wrap it in a container that will re-render whenever the store changes, then use the mapStateToProps function to work out which props to pass down to your original component.
const MyStore = { ... };
const MyComponent = React.createClass( ... );
function mapStateToProps(state) {
return { foo: state.bar.foo };
}
export default connect(MyStore, mapStateToProps, MyComponent);
setState is an asychronous method as it needs to be batched to keep React apps from being delaying repaints when they trigger lots of updates. You can reliably wait for the state to change by passing a callback as the second argument.
this.setState({ foo: 'bar' }, () => this.state.foo);

Can you force a React component to rerender without calling setState?

I have an external (to the component), observable object that I want to listen for changes on. When the object is updated it emits change events, and then I want to rerender the component when any change is detected.
With a top-level React.render this has been possible, but within a component it doesn't work (which makes some sense since the render method just returns an object).
Here's a code example:
export default class MyComponent extends React.Component {
handleButtonClick() {
this.render();
}
render() {
return (
<div>
{Math.random()}
<button onClick={this.handleButtonClick.bind(this)}>
Click me
</button>
</div>
)
}
}
Clicking the button internally calls this.render(), but that's not what actually causes the rendering to happen (you can see this in action because the text created by {Math.random()} doesn't change). However, if I simply call this.setState() instead of this.render(), it works fine.
So I guess my question is: do React components need to have state in order to rerender? Is there a way to force the component to update on demand without changing the state?
In class components, you can call this.forceUpdate() to force a rerender.
Documentation: https://facebook.github.io/react/docs/component-api.html
In function components, there's no equivalent of forceUpdate, but you can contrive a way to force updates with the useState hook.
forceUpdate should be avoided because it deviates from a React mindset. The React docs cite an example of when forceUpdate might be used:
By default, when your component's state or props change, your component will re-render. However, if these change implicitly (eg: data deep within an object changes without changing the object itself) or if your render() method depends on some other data, you can tell React that it needs to re-run render() by calling forceUpdate().
However, I'd like to propose the idea that even with deeply nested objects, forceUpdate is unnecessary. By using an immutable data source tracking changes becomes cheap; a change will always result in a new object so we only need to check if the reference to the object has changed. You can use the library Immutable JS to implement immutable data objects into your app.
Normally you should try to avoid all uses of forceUpdate() and only read from this.props and this.state in render(). This makes your component "pure" and your application much simpler and more efficient.forceUpdate()
Changing the key of the element you want re-rendered will work. Set the key prop on your element via state and then when you want to update set state to have a new key.
<Element key={this.state.key} />
Then a change occurs and you reset the key
this.setState({ key: Math.random() });
I want to note that this will replace the element that the key is changing on. An example of where this could be useful is when you have a file input field that you would like to reset after an image upload.
While the true answer to the OP's question would be forceUpdate() I have found this solution helpful in different situations. I also want to note that if you find yourself using forceUpdate you may want to review your code and see if there is another way to do things.
NOTE 1-9-2019:
The above (changing the key) will completely replace the element. If you find yourself updating the key to make changes happen you probably have an issue somewhere else in your code. Using Math.random() in key will re-create the element with each render. I would NOT recommend updating the key like this as react uses the key to determine the best way to re-render things.
In 2021 and 2022, this is the official way to forceUpdate a React Functional Component.
const [, forceUpdate] = useReducer(x => x + 1, 0);
function handleClick() {
forceUpdate();
}
I know the OP is for a class component. But the question was asked in 2015 and now that hooks are available, many may search for forceUpdate in functional components. This little bit is for them.
Edit 18th Apr 2022
It's usually bad practice to force update your components.
A few reasons that can cause the need to use force updates.
Not using state variables where you have to - local, redux, context.
The field from the state object you are trying to access and expecting to update/change is too deeply nested in objects or arrays. Even Redux advises to maintain flat objects or arrays. If only one field value changes in a complex object, React may not figure out that the state object has changed, thus it does not update the component. Keep your state flat and simple.
The key on your list items, as mentioned in another answer. In fact, this can cause other unexpected behaviors as well. I've seen lists where items are repeatedly rendered (duplicates) because the keys aren't identical or the keys are just missing altogether. Always request the backend team to send unique ids everywhere possible! Avoid using array indexes for keys. Do not try to create unique ids on the front-end by using nanoid, uuid or random. Because ids created using above methods change each time the component updates (keys provided to a list need to be static and the same on each render). Creating unique ids is usually a backend concern. Try your best to not bring that requirement to the front-end. The front-end's responsibility is only to paint what data the backend returns and not create data on the fly.
If your useEffect, useCallback dependency arrays do not have the proper values set. Use ESLint to help you with this one! Also, this is one of the biggest causes for memory leaks in React. Clean up your state and event listeners in the return callback to avoid memory leaks. Because such memory leaks are awfully difficult to debug.
Always keep an eye on the console. It's your best friend at work. Solving warning and errors that show up in the console can fix a whole lot of nasty things - bugs and issues that you aren't even aware off.
A few things I can remember that I did wrong. In case it helps..
Actually, forceUpdate() is the only correct solution as setState() might not trigger a re-render if additional logic is implemented in shouldComponentUpdate() or when it simply returns false.
forceUpdate()
Calling forceUpdate() will cause render() to be called on the component, skipping shouldComponentUpdate(). more...
setState()
setState() will always trigger a re-render unless conditional rendering logic is implemented in shouldComponentUpdate(). more...
forceUpdate() can be called from within your component by this.forceUpdate()
Hooks: How can I force component to re-render with hooks in React?
BTW: Are you mutating state or your nested properties don't propagate?
How to update nested state properties in React
Sandbox
I Avoided forceUpdate by doing following
WRONG WAY : do not use index as key
this.state.rows.map((item, index) =>
<MyComponent cell={item} key={index} />
)
CORRECT WAY : Use data id as key, it can be some guid etc
this.state.rows.map((item) =>
<MyComponent item={item} key={item.id} />
)
so by doing such code improvement your component will be UNIQUE and render naturally
When you want two React components to communicate, which are not bound by a relationship (parent-child), it is advisable to use Flux or similar architectures.
What you want to do is to listen for changes of the observable component store, which holds the model and its interface, and saving the data that causes the render to change as state in MyComponent. When the store pushes the new data, you change the state of your component, which automatically triggers the render.
Normally you should try to avoid using forceUpdate() . From the documentation:
Normally you should try to avoid all uses of forceUpdate() and only read from this.props and this.state in render(). This makes your application much simpler and more efficient
use hooks or HOC take your pick
Using hooks or the HOC (higher order component) pattern, you can have automatic updates when your stores change. This is a very light-weight approach without a framework.
useStore Hooks way to handle store updates
interface ISimpleStore {
on: (ev: string, fn: () => void) => void;
off: (ev: string, fn: () => void) => void;
}
export default function useStore<T extends ISimpleStore>(store: T) {
const [storeState, setStoreState] = useState({store});
useEffect(() => {
const onChange = () => {
setStoreState({store});
}
store.on('change', onChange);
return () => {
store.off('change', onChange);
}
}, []);
return storeState.store;
}
withStores HOC handle store updates
export default function (...stores: SimpleStore[]) {
return function (WrappedComponent: React.ComponentType<any>) {
return class WithStore extends PureComponent<{}, {lastUpdated: number}> {
constructor(props: React.ComponentProps<any>) {
super(props);
this.state = {
lastUpdated: Date.now(),
};
this.stores = stores;
}
private stores?: SimpleStore[];
private onChange = () => {
this.setState({lastUpdated: Date.now()});
};
componentDidMount = () => {
this.stores &&
this.stores.forEach((store) => {
// each store has a common change event to subscribe to
store.on('change', this.onChange);
});
};
componentWillUnmount = () => {
this.stores &&
this.stores.forEach((store) => {
store.off('change', this.onChange);
});
};
render() {
return (
<WrappedComponent
lastUpdated={this.state.lastUpdated}
{...this.props}
/>
);
}
};
};
}
SimpleStore class
import AsyncStorage from '#react-native-community/async-storage';
import ee, {Emitter} from 'event-emitter';
interface SimpleStoreArgs {
key?: string;
defaultState?: {[key: string]: any};
}
export default class SimpleStore {
constructor({key, defaultState}: SimpleStoreArgs) {
if (key) {
this.key = key;
// hydrate here if you want w/ localState or AsyncStorage
}
if (defaultState) {
this._state = {...defaultState, loaded: false};
} else {
this._state = {loaded: true};
}
}
protected key: string = '';
protected _state: {[key: string]: any} = {};
protected eventEmitter: Emitter = ee({});
public setState(newState: {[key: string]: any}) {
this._state = {...this._state, ...newState};
this.eventEmitter.emit('change');
if (this.key) {
// store on client w/ localState or AsyncStorage
}
}
public get state() {
return this._state;
}
public on(ev: string, fn:() => void) {
this.eventEmitter.on(ev, fn);
}
public off(ev: string, fn:() => void) {
this.eventEmitter.off(ev, fn);
}
public get loaded(): boolean {
return !!this._state.loaded;
}
}
How to Use
In the case of hooks:
// use inside function like so
const someState = useStore(myStore);
someState.myProp = 'something';
In the case of HOC:
// inside your code get/set your store and stuff just updates
const val = myStore.myProp;
myOtherStore.myProp = 'something';
// return your wrapped component like so
export default withStores(myStore)(MyComponent);
MAKE SURE
To export your stores as a singleton to get the benefit of global change like so:
class MyStore extends SimpleStore {
public get someProp() {
return this._state.someProp || '';
}
public set someProp(value: string) {
this.setState({...this._state, someProp: value});
}
}
// this is a singleton
const myStore = new MyStore();
export {myStore};
This approach is pretty simple and works for me. I also work in large teams and use Redux and MobX and find those to be good as well but just a lot of boilerplate. I just personally like my own approach because I always hated a lot of code for something that can be simple when you need it to be.
So I guess my question is: do React components need to have state in
order to rerender? Is there a way to force the component to update on
demand without changing the state?
The other answers have tried to illustrate how you could, but the point is that you shouldn't. Even the hacky solution of changing the key misses the point. The power of React is giving up control of manually managing when something should render, and instead just concerning yourself with how something should map on inputs. Then supply stream of inputs.
If you need to manually force re-render, you're almost certainly not doing something right.
There are a few ways to rerender your component:
The simplest solution is to use forceUpdate() method:
this.forceUpdate()
One more solution is to create not used key in the state(nonUsedKey)
and call setState function with update of this nonUsedKey:
this.setState({ nonUsedKey: Date.now() } );
Or rewrite all current state:
this.setState(this.state);
Props changing also provides component rerender.
For completeness, you can also achieve this in functional components:
const [, updateState] = useState();
const forceUpdate = useCallback(() => updateState({}), []);
// ...
forceUpdate();
Or, as a reusable hook:
const useForceUpdate = () => {
const [, updateState] = useState();
return useCallback(() => updateState({}), []);
}
// const forceUpdate = useForceUpdate();
See: https://stackoverflow.com/a/53215514/2692307
Please note that using a force-update mechanism is still bad practice as it goes against the react mentality, so it should still be avoided if possible.
You could do it a couple of ways:
1. Use the forceUpdate() method:
There are some glitches that may happen when using the forceUpdate() method. One example is that it ignores the shouldComponentUpdate() method and will re-render the view regardless of whether shouldComponentUpdate() returns false. Because of this using forceUpdate() should be avoided when at all possible.
2. Passing this.state to the setState() method
The following line of code overcomes the problem with the previous example:
this.setState(this.state);
Really all this is doing is overwriting the current state with the current state which triggers a re-rendering. This still isn't necessarily the best way to do things, but it does overcome some of the glitches you might encounter using the forceUpdate() method.
We can use this.forceUpdate() as below.
class MyComponent extends React.Component {
handleButtonClick = ()=>{
this.forceUpdate();
}
render() {
return (
<div>
{Math.random()}
<button onClick={this.handleButtonClick}>
Click me
</button>
</div>
)
}
}
ReactDOM.render(<MyComponent /> , mountNode);
The Element 'Math.random' part in the DOM only gets updated even if you use the setState to re-render the component.
All the answers here are correct supplementing the question for understanding..as we know to re-render a component with out using setState({}) is by using the forceUpdate().
The above code runs with setState as below.
class MyComponent extends React.Component {
handleButtonClick = ()=>{
this.setState({ });
}
render() {
return (
<div>
{Math.random()}
<button onClick={this.handleButtonClick}>
Click me
</button>
</div>
)
}
}
ReactDOM.render(<MyComponent /> , mountNode);
Just another reply to back-up the accepted answer :-)
React discourages the use of forceUpdate() because they generally have a very "this is the only way of doing it" approach toward functional programming. This is fine in many cases, but many React developers come with an OO-background, and with that approach, it's perfectly OK to listen to an observable object.
And if you do, you probably know you MUST re-render when the observable "fires", and as so, you SHOULD use forceUpdate() and it's actually a plus that shouldComponentUpdate() is NOT involved here.
Tools like MobX, that takes an OO-approach, is actually doing this underneath the surface (actually MobX calls render() directly)
forceUpdate(), but every time I've ever heard someone talk about it, it's been followed up with you should never use this.
forceUpdate(); method will work but it is advisable to use setState();
In order to accomplish what you are describing please try this.forceUpdate().
Another way is calling setState, AND preserve state:
this.setState(prevState=>({...prevState}));
I have found it best to avoid forceUpdate(). One way to force re-render is to add dependency of render() on a temporary external variable and change the value of that variable as and when needed.
Here's a code example:
class Example extends Component{
constructor(props){
this.state = {temp:0};
this.forceChange = this.forceChange.bind(this);
}
forceChange(){
this.setState(prevState => ({
temp: prevState.temp++
}));
}
render(){
return(
<div>{this.state.temp &&
<div>
... add code here ...
</div>}
</div>
)
}
}
Call this.forceChange() when you need to force re-render.
ES6 - I am including an example, which was helpful for me:
In a "short if statement" you can pass empty function like this:
isReady ? ()=>{} : onClick
This seems to be the shortest approach.
()=>{}
use useEffect as a mix of componentDidMount, componentDidUpdate, and componentWillUnmount, as stated in the React documentation.
To behave like componentDidMount, you would need to set your useEffect like this:
useEffect(() => console.log('mounted'), []);
The first argument is a callback that will be fired based on the second argument, which is an array of values. If any of the values in that second argument changed, the callback function you defined inside your useEffect will be fired.
In the example I'm showing, however, I'm passing an empty array as my second argument, and that will never be changed, so the callback function will be called once when the component mounts.
That kind of summarizes useEffect. If instead of an empty value, you have an argument, like:
useEffect(() => {
}, [props.lang]);
That means that every time props.lang changes, your callback function will be called. The useEffect will not rerender your component really, unless you're managing some state inside that callback function that could fire a re-render.
If you want to fire a re-render, your render function needs to have a state that you are updating in your useEffect.
For example, in here, the render function starts by showing English as the default language and in my use effect I change that language after 3 seconds, so the render is re-rendered and starts showing "spanish".
function App() {
const [lang, setLang] = useState("english");
useEffect(() => {
setTimeout(() => {
setLang("spanish");
}, 3000);
}, []);
return (
<div className="App">
<h1>Lang:</h1>
<p>{lang}</p>
</div>
);
}
You can use forceUpdate() for more details check (forceUpdate()).

Resources