How to use react context api with getDerivedStateFromProps? - reactjs

Context provides a way to pass data through the component tree without having to pass props down manually at every level. This is great!
but I'm wondering how to use it with getDerivedFromProps()
For example, if I have a prop sent via Context in top level of the app, that said it's window.location.href, and I need to take action in the child component based on the href, e.g. fetch the data.
Using getDerivedStateFromProps(), I have to write something like the following:
getDerivedStateFromProps(nextProps, state) {
var stateRev = null
var pathname = hrefToPath(nextProps.href)
if (pathname != state.pathname) {
stateRev = {}
Object.assign(stateRev, {
pathname,
book: source.find()
})
}
return stateRev
}
However, if I write the code like the above, I have to send the window.location.href through the levels. What I need to know is if the prop in the context changed, I need to update the state.
I see no way to know the prop in the context changed or not. Is there anything I need to know about the context api and getDerivedStateFromProps?
Thank you.

If you want to consume context in lifecycle methods you can use contextType. The problem with this approach is that getDerivedStateFromProps is static and cannot access instance variables.
So solution I see is to wrap your component in High Order Component, like this
const WithContext = (Component) => {
return (props) => (
<CustomContext.Consumer>
{value => <Component {...props} value={value} />}
</CustomContext.Consumer>
)
}
In this case you'll get context as part of props

getDerivedFromProps is not for that
DOCS - tips for getDerivedFromProps: 'If you need to perform a side effect (for example, data fetching or an animation) in response to a change in props, use componentDidUpdate lifecycle instead.'
Also 'This method doesn’t have access to the component instance.' - then no this.context available.
If you need to react on context prop change - use Context.Consumer. Use componentDidUpdate to compare props (consumer provides context value as prop) and conditionally fetch data.

Related

React userReducer init function not triggering on props update

I have a component which is a form which I use to create records in my database. I also want to be able to pass into this component values with which to populate the form, allowing me to then update my existing database records. Straightforward add/edit functionality from the same component.
The following code should explain how I am doing this. The media prop is an object containing the data. I have this data already in the parent element so setting the values here is fine and they pass thru without problem. However once the page is loaded the 3rd init argument of useReducer never re-triggers, and therefore my state cannot be overridden with the values passed down in the media prop. Is there a correct way to make the init function trigger when the props are updated, or is my issue architectural?
const MediaUploadForm = ({ media }) => {
const init = (initialState) => {
if (media) {
// ... here I extract the values I need and override the initialState where required
} else {
return initialState
}
}
const [formState, dispatch] = useReducer(
MediaFormReducer,
initialState,
init
)
So using the new React hooks features and keeping the component functional allows me to use useEffects() This is similar to using a componentDidUpdate type event. So the following code allows me to check for the status of a prop (media) and then dispatch an action that sets my redux state.
useEffect(() => {
if (media && id !== media.id) {
dispatch(loadMedia(media))
}
})
Thanks to #sliptype for pointing me in the right direction
Copying props into state is considered an anti pattern in React. Props changes do not trigger reinitialising state, as you have seen.
This is described in https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html.
From the recap it looks like the current suggested solution matches
Alternative 1: To reset only certain state fields, watch for changes in a special property (e.g. props.userID).
This is an alternative, rather than the recommendation.
https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#recap
Hope this link gives you more information around the topic, and the recommendations there help in future work.

How to handle render method until redux has returned results?

I am creating a component that fetches some apis to populate result.There are some transformations needed on those api results before rendering. I've used componentWillMount method to set redux state. I need to handle render method until redux those results to props. This takes some time. Since data is not available, render method will fail at that moment. I've tried setting default state but that doesn't seem to work since redux state will be mapped to props with mapStateToProps method. What is the correct and efficient approach to handle such case??
Thanks in advance
Normally you will render a different thing (or you won't render at all) when your data is not present.
{this.props.unreadMessages.length > 0 &&
<h2>
You have {this.props.unreadMessages.length} unread messages.
</h2>
}
or:
if (this.props.unreadMessages.length > 0) {
return <Main unreadMessages={this.props.unreadMessages} />;
} else {
return <Loading />
}
You can also have your default initial state and render based on that when props does not have data. maybe with something like:
<h2>
Hello, {this.props.username || this.state.username}.
</h2>
Also, the recommended lifecycle hook for Ajax call is componentDidMount.
If your data is not ready, you can use conditional rendering. Add additional indicator about your data, Like this
render() {
if (!this.props.data) {
return null;
}
return <YourComponent />;
}
And you'd better to fetch data in componentDidMount lifecycle.
I would check for the props in the render method with a ternary expression as a sort of guard clause. Something like this, for example:
render(){
return (<React.Fragment>
{ this.props.user ? <IntendedComponent ...this.props.user /> : <Loading user={defaultUser}/> }
<React.Fragment/>);
}
If the user prop has not been populated yet, it should be empty and display an alternate component (like the loading component in the example) or the intended component with default, hardcoded values. Once the data is in place, you can pass the data to a component as you see fit.
First of all componentWillMount is not a good method to call apis,
as per react documentation:
componentDidMount() is invoked immediately after a component is
mounted (inserted into the tree). Initialization that requires DOM
nodes should go here. If you need to load data from a remote endpoint,
this is a good place to instantiate the network request.
This method is a good place to set up any subscriptions. If you do
that, don’t forget to unsubscribe in componentWillUnmount().
Here is the link: https://reactjs.org/docs/react-component.html#componentdidmount
Here is what you can do till data is loaded:
get one flag into redux state called dataLoaded:false
Once the data is loaded you performed an operations you can easily change it.
And in render()
if(this.props.dataLoaded){
<loadingComponent></loadingComponent>
} else {
//operation is performed
}
Since you're using Redux, you can set the default Redux state in Reducers using ES6 default parameters. Something like this:
const apiStatus = (state='fetching', action) => {
// handle dispatch action
}
And in React, you can use Conditional Rendering to render a loading icon, for instance, while state is fetching. Then when state is finished, take the result and render the real elements.
To work with Async in Redux, see this link:
Async Action

Prevent react component from rendering twice when using redux with componentWillMount

I have a React component that dispatches a redux state change in its componentWillMount function. The reason is that when the component is loaded, it needs to get the id from the url (powered by react-router), and trigger an action that sets up the state with that id's data.
Here is the component:
class Editor extends React.Component {
componentWillMount() {
const { dispatch, params } = this.props
dispatch(editItem(params.id))
}
render() {
const item = this.props.item
console.log("Editing", item)
}
}
export default connect(state => ({item: state.item}))(Editor)
Here's the catch: render is getting called twice. item is undefined on the first call, and valid on the second. Ideally, it should only be called once this.props.item actually exists (after the editItem action has been dispatched and run).
According to the React docs: "If you call setState within this method, render() will see the updated state and will be executed only once despite the state change."
In redux, dispatch is the equivalent of calling setState, as it results in a state change. However, I'm guessing something in the way connect works is still causing render to be called twice.
Is there a way around this besides adding a line like if (!item) return; ?
One thing you might do is create a higher order component that handles the basic pattern of loading a different component (or no component) before the required props are loaded.
export const LoaderWrapper = function(hasLoaded, Component, LoaderComponent, onLoad) {
return props => {
if (hasLoaded(props)) {
return <Component {...props} />
}
else {
if (onLoad) onLoad(props)
return { LoaderComponent ? <LoaderComponent /> : null }
}
}
}
Then you can wrap your component before connecting it to get the desired behaviour.
export default connect(state => ({item: state.item}))(LoaderWrapper(
((props) => !!props.item),
Editor,
null,
(props) => props.dispatch(editItem(props.params.id))
))
You might want to add some currying magic to make sure you can compose these kinds of wrapper functions more nicely. Take a look at recompose for more info.
It looks like there's already an issue in the react-redux library.
https://github.com/rackt/react-redux/issues/210
What does editItem do? Does it add item to the redux state or is it there already?
If it is adding I imagine what is happening is that a render cycle happens with the current props, ie item being blank.
Then it gets rendered again when the props have changed, via setting the item.
One approach to fixing this sort of thing is to create a higher order component that wraps Editor and calls the dispatch action the rendering though is set either to a loading screen or and empty div until item is set. That way you can be assured that Editor will have an item.
But without knowing what editItem does it's sort of hard to know. Maybe you could paste the code for that?

Firing Redux actions in response to route transitions in React Router

I am using react-router and redux in my latest app and I'm facing a couple of issues relating to state changes required based on the current url params and queries.
Basically I have a component that needs to update it's state every time the url changes. State is being passed in through props by redux with the decorator like so
#connect(state => ({
campaigngroups: state.jobresults.campaigngroups,
error: state.jobresults.error,
loading: state.jobresults.loading
}))
At the moment I am using the componentWillReceiveProps lifecycle method to respond to the url changes coming from react-router since react-router will pass new props to the handler when the url changes in this.props.params and this.props.query - the main issue with this approach is that I am firing an action in this method to update the state - which then goes and passes new props the component which will trigger the same lifecycle method again - so basically creating an endless loop, currently I am setting a state variable to stop this from happening.
componentWillReceiveProps(nextProps) {
if (this.state.shouldupdate) {
let { slug } = nextProps.params;
let { citizenships, discipline, workright, location } = nextProps.query;
const params = { slug, discipline, workright, location };
let filters = this._getFilters(params);
// set the state accroding to the filters in the url
this._setState(params);
// trigger the action to refill the stores
this.actions.loadCampaignGroups(filters);
}
}
Is there a standard approach to trigger actions base on route transitions OR can I have the state of the store directly connected to the state of the component instead of passing it in through props? I have tried to use willTransitionTo static method but I don't have access to the this.props.dispatch there.
Alright I eventually found an answer on the redux's github page so will post it here. Hope it saves somebody some pain.
#deowk There are two parts to this problem, I'd say. The first is that componentWillReceiveProps() is not an ideal way for responding to state changes — mostly because it forces you to think imperatively, instead of reactively like we do with Redux. The solution is to store your current router information (location, params, query) inside your store. Then all your state is in the same place, and you can subscribe to it using the same Redux API as the rest of your data.
The trick is to create an action type that fires whenever the router location changes. This is easy in the upcoming 1.0 version of React Router:
// routeLocationDidUpdate() is an action creator
// Only call it from here, nowhere else
BrowserHistory.listen(location => dispatch(routeLocationDidUpdate(location)));
Now your store state will always be in sync with the router state. That fixes the need to manually react to query param changes and setState() in your component above — just use Redux's Connector.
<Connector select={state => ({ filter: getFilters(store.router.params) })} />
The second part of the problem is you need a way to react to Redux state changes outside of the view layer, say to fire an action in response to a route change. You can continue to use componentWillReceiveProps for simple cases like the one you describe, if you wish.
For anything more complicated, though, I recommending using RxJS if you're open to it. This is exactly what observables are designed for — reactive data flow.
To do this in Redux, first create an observable sequence of store states. You can do this using rx's observableFromStore().
EDIT AS SUGGESTED BY CNP
import { Observable } from 'rx'
function observableFromStore(store) {
return Observable.create(observer =>
store.subscribe(() => observer.onNext(store.getState()))
)
}
Then it's just a matter of using observable operators to subscribe to specific state changes. Here's an example of re-directing from a login page after a successful login:
const didLogin$ = state$
.distinctUntilChanged(state => !state.loggedIn && state.router.path === '/login')
.filter(state => state.loggedIn && state.router.path === '/login');
didLogin$.subscribe({
router.transitionTo('/success');
});
This implementation is much simpler than the same functionality using imperative patterns like componentDidReceiveProps().
As mentioned before, the solution has two parts:
1) Link the routing information to the state
For that, all you have to do is to setup react-router-redux. Follow the instructions and you'll be fine.
After everything is set, you should have a routing state, like this:
2) Observe routing changes and trigger your actions
Somewhere in your code you should have something like this now:
// find this piece of code
export default function configureStore(initialState) {
// the logic for configuring your store goes here
let store = createStore(...);
// we need to bind the observer to the store <<here>>
}
What you want to do is to observe changes in the store, so you can dispatch actions when something changes.
As #deowk mentioned, you can use rx, or you can write your own observer:
reduxStoreObserver.js
var currentValue;
/**
* Observes changes in the Redux store and calls onChange when the state changes
* #param store The Redux store
* #param selector A function that should return what you are observing. Example: (state) => state.routing.locationBeforeTransitions;
* #param onChange A function called when the observable state changed. Params are store, previousValue and currentValue
*/
export default function observe(store, selector, onChange) {
if (!store) throw Error('\'store\' should be truthy');
if (!selector) throw Error('\'selector\' should be truthy');
store.subscribe(() => {
let previousValue = currentValue;
try {
currentValue = selector(store.getState());
}
catch(ex) {
// the selector could not get the value. Maybe because of a null reference. Let's assume undefined
currentValue = undefined;
}
if (previousValue !== currentValue) {
onChange(store, previousValue, currentValue);
}
});
}
Now, all you have to do is to use the reduxStoreObserver.js we just wrote to observe changes:
import observe from './reduxStoreObserver.js';
export default function configureStore(initialState) {
// the logic for configuring your store goes here
let store = createStore(...);
observe(store,
//if THIS changes, we the CALLBACK will be called
state => state.routing.locationBeforeTransitions.search,
(store, previousValue, currentValue) => console.log('Some property changed from ', previousValue, 'to', currentValue)
);
}
The above code makes our function to be called every time locationBeforeTransitions.search changes in the state (as a result of the user navigating). If you want, you can observe que query string and so forth.
If you want to trigger an action as a result of routing changes, all you have to do is store.dispatch(yourAction) inside the handler.

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