Is it a good idea to store components in state? - reactjs

Is it good practice to store whole React Components in the component state or redux state? Yes, it's optional as we could store a string in the state and render the component conditionally but in some cases, it is simpler to just store the component in the state.
For example,
const [ components ] = useState([
{ id: 1, component: <Login />, title: `Login` },
{ id: 2, component: <Register />, title: `Register` },
])
But components can be large and I was wondering if that makes any difference. Is this a bad practice?

You ask:
"Is it a good idea to store components in state?
Is it good practice to store whole React Components in the component state or redux
state?
[...] but in some cases, it is simpler to just store the component in the state.
Is this a bad practice?"
No, it's not a good idea.
No, it's not good practice.
No, that is never simpler.
Yes, it's a bad practice, decidedly.
React is based upon one simple yet powerful core concept: a function receives some values (the properties) and returns the elements that are to be rendered as the immediate result of the current set of values. (All other concepts are only auxiliary: state, side-effects, lifecylce, synthetic events, refs, memoization, tachyon emitters and numberwang)
Storing elements for later use directly breaks this core concept. You risk having stale elements that do not correspond to the current set of properties. (And even if your elements do not have any properties as input you will not gain anything useful by putting them in the state, read on.)
And what is even the intended use of creating react elements and putting them in the state? What perceived problem do want to solve?
I can make out two things that you probably try to achieve by doing so: 1. having a means to conditionally render an element; and 2. somehow optimizing the number of times a new react element is created.
For both these aims the modus operandi of putting react elements into the state is ill fitted.
For both these aims React provides straightforward solutions:
Conditionally rendered elements are achieved thus:
const [showPrompt, setShowPrompt] = useState(false);
// ...
<div>
{ showPrompt && <ConfirmationPrompt /> }
</div>
or by using a prop
const MyComponent = ({showPrompt}) => {
// ...
<div>
{ showPrompt && <ConfirmationPrompt /> }
</div>
This is simple, it is clear, it is correct. Nothing breaks. No future maintainer is compelled to curse you or poison your vanilla chai latte.
If you have reason to think your render function is quite heavy and you want to reduce the number of times it is run, you can use the useMemo hook (to memoize calculation results within you render function) or you can wrap your whole component in React.memo() so it is only re-rendered when the props change. (You should employ both mechanisms only when you can actually measure any difference.)
const primeNumbers = useMemo(() => calculatePrimeNumbers(limit), [limit]);
const MyComponent = React.memo((props) => {
/* render using props */
});

Actually, it works but really it is not a good idea, it is very hard for ReactJS to compare it, right it in state object or modify it or delete it.
Use simple string for your state, store components in static object and then play with them:
const StaticList = {
Login, // <<== pay attention, I don't use JSX, I pass the imported name
Register,
};
const YourComponent = () => {
const [ components ] = useState([
{ id: 'one', cn: 'Login', title: `Login` },
{ id: 'two', cn: 'Register', title: `Register` },
]);
return (
<div>
{components.map(({ id, cn, title }) => {
const Comp = StaticList[cn];
return (
<div key={id}>
<span>{title}</span>
<Comp />
</div>
);
})}
</div>
);
Something like above, it is a simple sample.

Related

What is the best possible way to pass data between child components in React?

I am curious to know if the performance of a React app will be affected if we have multiple nested components and we pass a callback function to the lowest-level component and then the entire component tree is rendered from an event in the lowest-level component that triggers this callback.
If so, what are the best possible ways to tackle this?
Thanks.
There are plenty of ways to pass data, but less so for functions.
Redux could be used, or useContext, or some other external solution like jotai.
Actually, the recommended official way is useContext: https://reactjs.org/docs/hooks-faq.html#how-to-avoid-passing-callbacks-down
const TodosDispatch = React.createContext(null);
function TodosApp() {
// Note: `dispatch` won't change between re-renders
const [todos, dispatch] = useReducer(todosReducer);
return (
<TodosDispatch.Provider value={dispatch}>
<DeepTree todos={todos} />
</TodosDispatch.Provider>
);
}
function DeepChild(props) {
// If we want to perform an action, we can get dispatch from context.
const dispatch = useContext(TodosDispatch);
function handleClick() {
dispatch({ type: 'add', text: 'hello' });
}
return (
<button onClick={handleClick}>Add todo</button>
);
}
But frankly, passing a function several components down isn't so bad either.
It makes data dependencies more explicit and improves testability, although it's a bit verbose indeed. Do what you feel is more right. You can change your approach later (in this case) pretty easily.

Passing one Prop vs Many Props vs Nested Components in React?

This is a part of Think in React.
Thinking in React is the hard part for me because I see many developers do React with different mindsets.
When I was writing code for the Notification component that will be used by developers, suddenly I've noticed that there are different experiences to use the component:
Passing many Props like Bootstrap
<Notification
title="New Feature"
body={message}
action={action}/>
Passing one Prop as an Object
const data = {
title:"",
subtitle:"",
message:""
}
<Notification data={data}/>
Passing nested Children
<Notification>
<Title></Title>
<Body><Body/>
<Action><Action>
</Notification>
I followed the passing nested Children because ( I guess) It seems if I scale the component, I don't need to provide a Bootstrap-like experience for the developers.
import React from "react"
import { Wrapper, Text } from "./Styled"
const Body = ({ message }) => (
<Wrapper>
<Text>{message}</Text>
</Wrapper>
)
export default Body
The problem is I'm thinking about it is when I want to scale the Component and let's say adding 3 additional features that require 3 additional props
I'm confused about the reasons why each approach might be chosen, and what's the "best" developer experience.
To answer this question let's review all possibilities given React Element and a Function Component:
const c1 = <div>React Element</div>;
const C2 = () => <div>Function Component</div>;
Notice that from performance perspective, every component can be memoized and not cause useless renders.
Pass React element through props
const ObjectProps = ({ component }) => {
console.log("render object props");
return component;
};
<ObjectProps component={c1} />
Pros
Simple.
Lets you define the exact usage of passed component (contract).
For example you might decide "messages" have to be on top of "actions"
Cons
No lazy rendering
Passing heavy component may cause performance issues.
Hard to inject props (need to use React.cloneElement).
As a library writer you would like to inject your styles and refs.
Passing Function Component
const FunctionComponentProps = ({ FunctionComponent }) => {
console.log("render function component");
return <FunctionComponent />;
};
<FunctionComponentProps FunctionComponent={C2} />
Pros
Lazy rendering.
Easy to pass props and inject props for inner implementation.
Lets you define the exact usage of passed component (contract).
Cons
Confusing
Children Render
const ChildrenRender = ({ children }) => {
console.log("render function component");
return children;
};
<ChildrenRender>
{c1}
<C2 />
</ChildrenRender>
Pros
No restriction for the developer (no contract)
Cons
Hard to inject children (need to use React.Children API + React.cloneElement combo)
No contract
The developer might pass "buttons" and then "messages" and break the view.
Implementing ChildrenRender usually comes with component instances which results a minimal "contract" mentioned above.
const ChildrenRender = ({ children }) => {...};
ChildrenRender.InnerComp1 = <SomeComponent .../>
ChildrenRender.InnerComp2 = <SomeComponent2 .../>
<ChildrenRender>
<ChildrenRender.InnerComp1>{c1}</ChildrenRender.InnerComp1>
<ChildrenRender.InnerComp2><C2/></ChildrenRender.InnerComp2>
</ChildrenRender>
In Conclusion
It heavily depends on the component's usage, usually the hybrid approach suits well - passing components through props and add an option for passing children too.
Another technique is Render Props.

Is there a reason I have to export React components as JSX or can I export them as objects that contain the JSX and other things?

TL;DR: I'm wondering if a solution I've thought of will cause performance issues.
The title is a bit confusing so let me clarify. A standard way of defining my header button component would be something like this (the code is using typescript but it's not relevant to the problem so you can probably ignore it):
import React from "react";
const HeaderButton = (props: Client.Common.Header.HeaderButton.Import) => {
const service = useHeaderButton();
return <div>
//header component JSX here
<div>
};
export default HeaderButton;
I want to change it up a bit. I've found I would much rather be able to expose some internal component methods to the parent component. In this case, I would like to be able to provide a toggle method to the parent instead of using an "active" property to determine the active state of the button. My reasoning is that I'd rather avoid having to set up the toggle logic in every parent component I use my HeaderButton in if I can instead define it in my button component and then have the parents use that method.
I've done this and it works as I'd like it to (so far, at least). I'm relatively new to both React and programming in general and self taught so I have gaps in my knowledge. I'm not that knowledgeable about React under the hood and how it does its performance optimizations etc so I'm worried I've done something that will cause unpredictable issues. Here's what I've done:
//Header.tsx (this component is using the regular style)
import React, { useEffect } from "react";
import "./Header.scss";
import HeaderButton from "./Components/HeaderButton/HeaderButton";
const Header = () => {
const HeaderButtonService = HeaderButton({ renderProps: <div>TEST</div>, class: "languageSelectionButton" });
useEffect(() => {
setTimeout(() => {
HeaderButtonService.toggle();
}, 5 * 1000);
}, []);
return (
<div id="headerBar">
<div className="headerNavigationButtonsContainer"></div>
{HeaderButtonService.view}
</div>
);
};
export default Header;
//HeaderButton.ts
import HeaderButtonView from "./HeaderButtonView";
const HeaderButton = (props: Client.Common.Header.HeaderButton.Import) => {
const service = useHeaderButton();
return {
toggle: service.toggleActive,
view: HeaderButtonView({ ...props, service.active })
};
};
export default HeaderButton;
//HeaderButtonView.tsx
import React from "react";
import "./HeaderButton.scss";
const HeaderButtonView = (props: Client.Common.Header.HeaderButton.Import) => {
return (
<div className={"headerButton" + (props.active ? " active" : "")
+ (props.class ? " " + props.class : "")}
style={{ ...props.style }}>
{props.renderProps && props.renderProps}
</div>
);
};
export default HeaderButtonView;
In my solution, I import HeaderButton.ts instead of HeaderButton.tsx. The parent component passes the relevant props to HeaderButton.ts which passes them down to HeaderButtonView.tsx while adding the "active" prop which it gets from a custom hook, not the parent component. It then returns the result of invoking HeaderButtonView with these new props as well as the method to toggle the active state.
This is a simple example but I would potentially use this template to expose state values and multiple methods to parent components.
The code works, it renders what I want it to render and toggles the active state after 5 seconds.
My concern is that, not knowing all that much about how react works under the hood, I might have created an optimization problem. Is there any reason I shouldn't be using this pattern?
I have done some testing and it doesn't seem to break anything. I added a counter to the state of header.tsx and increment it every 3 seconds then watch for re-renders. I was concerned that react would not be able to recognize that the old and new HeaderButton components are the same but it did. Though react goes through the component tree, it doesn't re-render the button (except after the first 5 seconds when activity is toggled).
Also, should HeaderButton.ts be a hook? It's working as intended atm so I'm not sure what exactly I gain/lose from adding "use" in front of it.
Your approach here is 1) contrary to how 99% of people use React, 2) contrary to very way React is intended to be used, and 3) overcomplicated in a way that adds a level of abstraction to React that absolutely does not need to be there.
1) This is just not how people write React code. Sure it might work for you and make sense on some level but no one else follows this pattern. What about when you start importing and using other people's components? What about when you have to partner with someone else to write an app? What about when you hand off or are handed off a bunch of code that is patterned in a completely different way? There is absolutely a lot to be said for following prevailing (or at least common) coding patterns because it makes your code a lot more interoperable and easy to understand compared to the rest of the framework ecosystem, and vice-versa.
2) React at its very core is intended to be declarative. It is the number one adjective most people would use to describe it, and features heavily on the very front page of the React website. Your proposed pattern here is very un-declarative, and directly defeats not just the declarative nature of React but the inherent patterns of component state and props. I could link you to examples in the documentation as to how declarative coding, state management, and props feature heavily in React design patterns but the list would include practically every page in the website: Components and Props, State and Lifecycle, Lifting State Up, Thinking in React, etc etc.
3) Your proposed pattern is just... needlessly complicated and abstract. It adds a layer of confusion that does not actually make things easier. I can barely follow even your basic minimal example code!
Your core rationale seems to be this:
My reasoning is that I'd rather avoid having to set up the toggle logic in every parent component I use my HeaderButton in if I can instead define it in my button component and then have the parents use that method.
That's a great instinct - make things reusable and modular so that you don't have to repeat yourself too often. Well, you can do that beautifully in React while adhering to the tenants of React's declarative nature!
First let's rewrite your components in a way that is a more "traditional" React style - just make the <HeaderButton> a regular component that accepts an active prop, and storing that state in the parent, <Header>. This is called "lifting state up" and is a key concept in React - state should live at the lowest common denominator that allows the necessary components access to it. In this case the parent <Header> needs access to the state, because it needs to not only pass it into <HeaderButton> as a prop, but be able to modify that state:
const HeaderButton = ({active}) => {
return <div>{active ? 'Active' : 'Inactive'}</div>
};
const Header = () => {
const [active, setActive] = React.useState(false);
const toggleActive = () => {
setTimeout(() => {
setActive(oldActiveState => !oldActiveState);
}, 5 * 1000);
};
return (
<header>
<button onClick={toggleActive} >Toggle active state</button>
<HeaderButton active={active} />
</header>
);
}
Cool, now state lives in the parent, in can modify that state, and it passes that state as a prop to <HeaderButton>. It is very declarative, easy to understand, and it's clear where state lives and what component is rendering what.
Now on to your concern about reusing the toggle logic. What if we want to use <HeaderButton> somewhere else and have the same toggle logic? What if we want to have five header buttons inside of <Header>? Do we need to copy and paste the same logic many times?
React provides a great solution here with custom hooks. Custom hooks allow you to encapsulate logic and state in a clean way. And - this is very important - the state it encapsulates still lives inside of the component that calls the custom hook. This means we can encapsulate the state and logic but they will still "live" inside of <Header> so we have access to it to pass as a prop. Let's try it:
const useHeaderButtonState = () => {
const [active, setActive] = React.useState(false);
const toggleActive = () => {
setTimeout(() => {
setActive(oldActiveState => !oldActiveState);
}, 5 * 1000);
};
return [active, toggleActive];
}
const HeaderButton = ({active}) => {
return <div>{active ? 'Active' : 'Inactive'}</div>
};
const Header = () => {
const [active, toggleActive] = useHeaderButtonState();
return (
<header>
<button onClick={toggleActive} >Toggle active state</button>
<HeaderButton active={active} />
</header>
);
}
Now, the state and the toggle logic live inside of useHeaderButtonState(). When called, it returns both a value (active) and a function for updating that value (toggleActive). Inside of <Header>, we can deconstruct the result of the custom hook call and use it to render.
We could even extend this custom hook even further to return not just the state and updater function, but a component to render. Then, if we want to render multiple instances of a component, including all its associated state and logic, and still have access to the state and logic in the parent component (<Header>), we can do that:
const useHeaderButtonState = () => {
const [active, setActive] = React.useState(false);
const toggleActive = () => {
setTimeout(() => {
setActive(oldActiveState => !oldActiveState);
}, 5 * 1000);
};
const headerButtonComponent = (
<>
<button onClick={toggleActive}>Toggle active state</button>
<HeaderButton active={active} />
</>
);
return [headerButtonComponent, active, toggleActive];
};
const HeaderButton = ({ active }) => {
return <div>{active ? "Active" : "Inactive"}</div>;
};
const Header = () => {
const [
headerButtonComponent1,
active1,
toggleActive1
] = useHeaderButtonState();
const [
headerButtonComponent2,
active2,
toggleActive2
] = useHeaderButtonState();
const [
headerButtonComponent3,
active3,
toggleActive3
] = useHeaderButtonState();
return (
<header>
{headerButtonComponent1}
{headerButtonComponent2}
{headerButtonComponent3}
</header>
);
};
https://codesandbox.io/s/pensive-sutherland-2onx9
Now we're cooking with gas! We're reusing state and logic but doing it in a declarative way that makes sense inside of React.
Sorry to be so heavy handed but I really, really want to discourage you from using the imperative pattern you proposed above. I've been writing React code for 3+ years and trust me when I say that sticking to the established patterns of React will pay off in the long run. Not only is it legitimately easier to write and comprehend, if will help your career to write code that other devs can more easily understand and work with.
I conduct a lot of hiring interviews and if I saw someone submit the code you wrote above, I would think they have no idea how React works or is intended to work and would immediately disqualify them. If you find it difficult or counterintuitive to understand, I would suggest keep on learning and practicing (with a more declarative React-compatible style) until it clicks. Otherwise, perhaps React just isn't the framework for you and you'd be best served with a different framework that more closely matches your preferences, style, and mental models!
Good luck!
Edit: Oh and one last thing I'll touch upon. You mentioned concerns about performance. In this case the performance differences are actually completely negligible and not worth even considering. In general React is very well optimized on its own and you don't need to worry about performance except in very specific edge cases. You should typically only optimize if and when you actually run into a performance bottleneck, and you solve for that. As they say, premature optimization is the root of all evil.
My response instead addresses the core programming pattern that you are proposing here on the basis that it makes the process of developing, debugging, and understanding the code itself needlessly difficult.

Is this a correct use case for the React useImperativeHandle hook?

I'm learning React hooks (and I'm fairly new to React), and was thinking through use cases for useImperativeHandle. I came up with a pretty useful scenario.
I do know that this can be accomplished without useImperativeHandle, but I think there are some advantages here.
What I don't know...
Have I 'discovered the obvious' and this isn't really useful? ... or
Is this bad form, or an anti-pattern?
My code is working- but I'm looking for input about best practices. Also, since there's a dearth of information right now about useImperativeHandle, this example that goes beyond an input ref might be useful to others.
I have a minimal example posted on Github if you want to play with it:
https://github.com/ericsolberg/react-uih-hook
Using markup similar to:
const App = props => {
return (
<Foo>
<Bar>This is Bar 0</Bar>
<Bar>This is Bar 1</Bar>
<Bar>This is Bar 2</Bar>
</Foo>
);
};
What I've accomplished is:
Allow a parent component 'Foo' to provide state storage for it's children - so Foo can mount/dismount children yet allow them to restore state.
'Bar' uses useImperativeHandle to provide a 'call-in' so Bar can veto being dismounted, in case it is doing something important.
As I noted, this works perfectly. In React, data and props flow down the tree, while callbacks flow up. This gives you a way, for specific scenarios, to call down the tree. Is it advisable?
This is an anti-pattern: Inject props into the children without explicitly passing the props.
The idiomatic options are:
pass props explicitly
use context
Lifting state up to be able to do the above 2
render props
So if nothing simpler would be suitable for my business logic, I do something like following to avoid invisible tight coupling between Foo and Bar:
const App = () => (
<Foo>
{({selector, saveStateFactory}) => (<>
<Bar state={selector(0)} saveState={saveStateFactory(0)} />
<Bar state={selector(1)} saveState={saveStateFactory(1)} />
</>)}
</Foo>
)
const Foo = () => {
const [state, dispatch] = useReducer(...)
const selector = (id) => state[id]
const saveStateFactory = (id) => {
return (payload) => dispatch({type: id, payload})
}
// do something with whole state that cannot be done in App nor Bar
return children({selector, saveStateFactory})
}

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