connect(null, null, null, { withRef: true }) unconventional? - reactjs

I have a need to pull some content out of a child component such that the parent (or just generally higher-level) component can dispatch an action creator with the data of the inner child component.
Previously I was invoking a function on the child ref like this.refs.child.doStuff() until I found it was becoming difficult to keep track of the ref in whatever parent component I needed the method data in. Everytime there is a HOC or some other kind of compositing wrapper I need to add more code to pass the ref up the chain that I need. Not to mention there was a lot of duplicate code in each child function that was standard to all.
// child.js
class InnerComponent extends React.PureComponent {
doStuff = () => {
calculateFrom(this.state.content)
// and then do stuff with it..
// I've since made this more redux-y by just returning the data
// out to an action dispatcher
}
}
const SomeHOC = (args) => {
return (Component) => class extends React.Component {
proc(wrappedComponentInstance) {
// I went with using the getWrappedInstance func here
// to mimic conect(.., .., .. { withRef: true }) for
// basic compatibility with a higher level function that
// dives through the wrappedInstances to get to the bottom one
this.getWrappedInstance = () => wrappedComponentInstance;
}
render() {
const props = Object.assign({}, this.props, { ref: this.proc.bind(this) });
return <Component {...props} {...this.state} />
}
}
}
// I actually don't need the connect() here but will on other components of the same style when they're not in their wrapped form
export default connect(..., ..., null, { withRef: true })(SomeHOC()(InnerComponent);
// parent container (there are multiples of these in my app)
class Container extends React.PureComponent {
determineContent = (...) => {
return React.createElement(this.state.content, {
// so I can get to the composited inner element...
ref: (element) => { this._compositeElement = element; },
...viewletProps
});
}
componentWillMount() {
// ...
System.import(`${dynamic}.jsx`).then((content) => {
this.setState({ content });
});
}
renderButtonContainer = () => {
// Here's where things get weird...
if (!this.doStuff) {
this.doStuff = ((() => {
// hook into my ref
const compositeElement = this._compositeElement;
// deep-dive through to get to the base
let base;
if (compositeElement && typeof compositeElement.getWrappedInstance === 'function') {
base = this._compositeElement.getWrappedInstance();
while (typeof base.getWrappedInstance === 'function') {
base = base.getWrappedInstance();
}
if (typeof compositeElement.doStuff === 'function') {
this.doStuff = base.doStuff;
}
}
})());
}
return (
<div hidden={!this.doStuff}>
<span><i class="myIcon" onClick={this.doStuff}></i></span>
</div>
);
}
render() {
<div>
{ this.renderButtonContainer() }
<div>
{ this.determineContent(...) }
</div>
</div>
}
}
I've since pulled all of that out and am now dispatching an action per Redux-style and letting my reducer handle what needs to happen (just a synchronous internal call that executes immediately using the data in the action; I'm still unsure if this being in the reducer is bad form)
However, since I need my child components to still return the invocation of their doStuff() (getStuff() instead) at the time of the parent component's choosing, I find myself stuck with my same ref issues.
Am I going about this all wrong? It almost seems to me like for each child component I have I need to be storing this always-changing data from getStuff() inside my state model and just pass it down into the component? But I'd anticipate that would be too disassociating from the actual component and the rest of my app doesn't really care about that.

Frankly, none of that seems like a good idea at all. Even if it's technically possible, it completely goes against the intended usage of both React and Redux.
Refs in general are an escape hatch, and should be used only if necessary. Refs to DOM nodes are more useful, because you may need to do things like determining if a click is inside a DOM node, or read a value from an uncontrolled input. Refs to components have a lot fewer use cases. In particular, directly calling methods on components is definitely not idiomatic React usage, and should be avoided if at all possible. From there, groveling into the guts of React's implementation is a really bad idea.
If you need to make use of a nested child's data in a parent component, and those are widely separated, then you should either pass some kind of callback prop down through all those children, or you should dispatch a Redux action to put the data into the store and have the parent subscribe to that data.
So yeah, it's hard to tell exactly what you're actually needing to get done from that description and example code, but I can safely say that what you've got there is not the right way to do it.

#markerikson there is valid reasons for doing this. React unidirectional data flow is a very important fundamental rule, but just like how the best music breaks some rules (jazz improv), so too do the best React apps break some conventions in very crafty places. For example, Redux and React router must break convention by using context. So too is there valid use cases for using refs and accessing components with refs.
For example: Imagine you have a very complicated text editor component as a parent. You have many child components responsible for the fields such as a contact field a website field and a description field etc. Now, inside each of these child components you frequently update state. Specifically, on every single click of a button you update the state. The product ask is to have all of the fields alive at the same time so you cannot have individual submit buttons on the fields. You must take all of the current state of all fields at the same time. Now, you could manage the state by lifting it to the complicated parent component, but then that means every single time you press a button to re-render any of these fields you are going to have a callback that causes the complex parent to rerender and therefore all other children components.
On the other hand, with the crafty use of the nifty trick component refs we can update the state of all fields completely independently without triggering rerenders in everything, then when the user submits the form we can trigger one function inside the parent that checks the current/final state of all children through refs and submit that value to the database.
The idea of antipatterns comes up in React a lot. You are right to stick to the rules most of the time, but like a jazz pianist takes the standard Saints Go Marching In and breaks some rules to make something more enjoyable to some people, we can take React, a rigid set of conventions and improv on them if it creates a better experience for certain contexts.

Related

Middleware-like (chain of responsibility) rendering of React components

I have a ParentComponent with mildly complex state and logic (mostly to make UI much easier), it can be described as a some sort of form.
Now, after user has entered all the inputs, he needs to press a button.
Button and its action depend on the current state:
If user hasn't logged in, button suggests user to do so.
If they "used account from another platform", button asks him to change the account.
If account has no permissions to perform "main" action, button advises to request required permission.
And so on...
Naturally, I could dump all this logic and validation into ParentComponent and be done with it (and also with maintainability and readability). But I'd like to factor out each step described above into its own component (that may use local hooks and some global state from Redux) since calling hooks conditionally is frowned upon and hooks I need in one component conflict with hooks from another.
So I'm faced with the following problem:
I have multiple child components, each of which returns
button, if user needs to do something.
null, otherwise (user meets condition, so everything is fine).
I want to render said components one-by-one in predefined order.
If child component renders to something, show it.
If component renders to null, I want to render the next child.
This resembles chain of responsibility which is exactly what I'd like to do, but I don't understand how this approach maps to React components and rendering.
I've managed to come up with the implementation that allows to do the following:
<ChainOfResponsibility>
<EnsureUserHasLoggedIn>
<EnsureBalanceIsLoaded>
<BuyItemButton>
</ChainOfResponsibility>
If <EnsureUserHasLoggedIn> figured that user needs to sign in, it will render only the "sign in" button and nothing else. Otherwise, it will render the next "piece" in chain.
interface Props extends ChainOfResponsibilityPiece {}
const EnsureUserHasLoggedIn: React.FC<Props> = ({ next }) => {
const userHasLoggedIn = /* Arbitrary condition. */ true
if (!userHasLoggedIn) {
return <Button>Sign In</Button>
}
// Render the next piece in chain.
return <>{next}</>
}
Nice thing about this approach is that rerender of previous children will trigger render of the parent component (ChainOfResponsibility). That, in turn, will render the whole chain again, ensuring all invariants from the beginning.
Here is the code if you need it:
import React from 'react'
export interface ChainOfResponsibilityPiece {
next?: React.ReactNode
}
interface ChainOfResponsibilityProps {
children: React.ReactNode
}
const ChainOfResponsibility: React.FC<ChainOfResponsibilityProps> = ({ children }) => {
// Safety.
const childrenArray = React.Children.toArray(children).filter((child) => React.isValidElement(child))
// No children -> nothing to render.
if (childrenArray.length === 0) {
return null
}
// Build children with references to the next child.
const childrenWithNext = []
childrenArray.reverse().forEach((child, childIndex) => {
if (!React.isValidElement(child)) {
throw new Error(`Children must be valid React elements, got ${child}`)
}
childrenWithNext.push(
React.cloneElement(child, {
// SAFETY: We want either next child or `undefined` – out-of-bounds index returns `undefined`.
next: childrenWithNext[childIndex - 1], // Reversed index.
}),
)
})
// Render. Children are reversed, so start with the last one.
return <>{childrenWithNext[childrenWithNext.length - 1]}</>
}
export default ChainOfResponsibility

React: Passing child state to parent state

I am working since more than a year with React and i have read Thinking in react, Lifting state up, and State and lifecycle.
I have learned that React's concept with data flow is is One-way data flow.
Citates from these pages:
React’s one-way data flow (also called one-way binding) keeps everything modular and fast.
Remember: React is all about one-way data flow down the component hierarchy. It may not be immediately clear which component should own what state. This is often the most challenging part for newcomers to understand, so follow these steps to figure it out:...
If you imagine a component tree as a waterfall of props, each component’s state is like an additional water source that joins it at an arbitrary point but also flows down.
As i understand this, following example is not allowed because i am passing child state data to the parent. But i see some developers working like that:
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = { fromParent: null };
}
addSomething(stateValueFromChild) {
this.setState({fromParent: stateValueFromChild});
}
render() {
return <Child
addSomething={(stateValueFromChild) => this.addSomething(stateValueFromChild)}>
// ...
</Child>;
}
}
class Child extends React.Component {
constructor(props) {
super(props);
this.state = { fromChild: 'foo' };
}
render() {
return <Form onSubmit={() => this.props.addSomething(this.state.fromChild)}>
// ...
</Form>;
}
}
My questions now are:
Is this really not allowed?
Why should this not be modular and fast?
Is this really braking the one-way-dataflow, becoming a two way dataflow?
What other problems could happen with this way?
When i would lift the state up, how would you solve following case; 50 concrete parents that uses that child component, should every parent have a same initialized sub-state for the same child that they are using?
Is this really not allowed?
Why should this not be modular and fast?
Excellent questions. This is allowed. It's just a bit tricky to make it work right because you've got state synchronization here. In the modern frontend world, the state synchronization is believed to be a very challenging task.
The problem will appear when you'll need to sync the state in two directions. For instance, if this child form is used to edit some element of the list, and you're changing the current element of the list. You'll need the child component to detect that situation and sync its local state with the new element from props during the particular UI update. As long as you don't have that, you're fine.
Is this really braking the one-way-dataflow, becoming a two way dataflow?
Nah, it's not. It's still unidirectional data flow because React can't work in any other way by design; UI updates always coming from top to bottom. In your example, your child triggers an event which causes the parent to update its state (it's totally fine), which will cause the UI update of the parent and the child. If you really violate "unidirectional data flow", you'll feel it. You will get an infinite loop or something similar.
When i would lift the state up, how would you solve following case; 50 concrete parents that uses that child component, should every parent have a same initialized sub-state for the same child that they are using?
Yep, that's what they mean by "lifting the state". You organize your root state as a tree reflecting the state of the children, then pass down elements of the state to children along with callbacks to modify the root state.
It's allowed and there is nothing wrong with your code, but I would not call it passing state from child to parent. All you do is invoking method passed in props and triggered by event with some argument, which in your example is child's state value, but it could be any other variable. Parent component knows nothing about nature of this argument, it just receives the value and able to do anything with it, for example change it's own state to another. If Child's state will change, Parent is not going to receive this update without onSubmit event fired again. But children always receive updates from the parent and automatically get rerendered, when props get changed. And of course some of the props could be states of some parents. Here is the major difference in behavior.
There is a good article explaining this in details: Props down, Events Up
Your question is absolutely correct, many times developer (including myself) struggled for passing child's components state or props to parent component.
I always do logic for getting next state or next props in child component and pass next state or next props to parent component by using handler functions of parent component.
import React, { Component } from "react";
import { render } from "react-dom";
class Parent extends Component {
constructor(props) {
super(props);
this.handleSomething = this.handleSomething.bind(this); // binding method
this.state = {
fromParent: "foo"
};
}
handleSomething(value) {
this.setState(prevState => {
return {
fromParent: value
};
});
}
render() {
return (
<div>
<h1>State: {this.state.fromParent}</h1>
<Child handleSomething={this.handleSomething} />
</div>
);
}
}
class Child extends Component {
constructor(props) {
super(props);
this.state = {
fromChild: "bar"
};
}
render() {
return (
<div>
<button
onClick={e => {
const { fromChild } = this.state;
// do whatever as per your logic for get value from child pass to handleSomething function
// you can also do same for handling forms
this.props.handleSomething(fromChild);
}}
>
Click Me
</button>
</div>
);
}
}
render(<Parent />, document.getElementById("app"));

Should React lifecycle methods be implemented in container components or presentation components?

I'm attempting to implement container components in React and Redux, and I'm unsure of what should take responsibility for lifecycle methods; containers or presentational components. One could argue that the lifecycle methods are presentational as they control DOM updates, but in that respect, aren't they also behavioural?
Furthermore, all of the implementations of container components that I've seen thus far utilise the react-redux bindings, as do my own. Even if I keep the concerns clearly separated, is it appropriate to inherit from React.Component in the case of a behaviour component?
For example, the app on which I'm working has a Tab presentational component, with a shouldComponentUpdate method:
class Tabs extends Component {
shouldComponentUpdate(nextProps) {
const { activeTab } = this.props;
return activeTab !== nextProps.activeTab;
}
[...]
}
On the one hand, this seems like a presentational concern as it controls when component should re-render. On the other hand, however, this is a means of handling when the user clicks a new tab, updating the application's state via an action, thus I'd class this as behavioural.
Data should be controlled as close to the root of the tree as possible. Doing this provides some simple optimizations, being that you're only passing what you need.
This will bubble down to where you are controlling some lifecycle components. As mgmcdermott mentioned, a lot of lifecycle components really depend on what you're doing, but the best case scenario is to have the simplest, dumbest components.
In most of my projects, in my react directory, I have components/ and views/. It is always my preference that a view should do as much of the grunt work as possible. That being said, there a a number of components that I've built that use lifecycle methods like componentDidMount, componentWillMount, componentWillUnmount, but I typically try and isolate updates in my views, since one of their jobs, in my opinion, is controlling data flow. That means componentShouldUpdate would live there. Personally, I think componentShouldUpdate is purely end-of-the-line optimization, though, and I only use it in cases where I'm having large performance issues during a re-render.
I'm not super sure I understand your "inherit from React.Component" question. If you're asking whether or not to use pure functions, es6 class, or React.createClass, I don't know that there is a standard rule, but it is good to be consistent.
To address whether or not you are dealing with a behaviour or presentation, behaviour is the click, but re-drawing is presentation. Your behaviour might be well off to exist in your Tab component, where the re-draw in your Tabs view. Tabs view passes your method from redux to set the currently active tab into your individual Tab components, and can then send the behaviour of tab switching through redux so you can do your presentation componentShouldUpdate. Does that make sense?
So your mapToDispatch method in your container will have a function to set your active tab, let's call it activateTab(idx), which takes a 0-based index of the tab. Your container passes that to the containing component that you control, which is views/Tabs, and it passes that method along to components/Tab. components/Tab will have an onClick method which is listening on one of your DOM elements, which then calls this.props.activateTab(myIndex) (you could also pass a bound version of activateTab into components/Tab so it does not have to be aware of it's own index), which triggers redux, then passes back your data into views/Tabs which can handle a componentShouldUpdate based on the data from redux.
Expanded Edit: Since this was marked as accepted, I'll blow out my code example into something usable to the average person.
As a quick aside, I'm not going to write much redux, as this can be very app dependent, but I'm assuming that you have a state with activeTabIdx hanging off the parent.
containers/TabExample.jsx
import { connect } from 'react-redux'
import Tabs from 'views/Tabs.js'
const mapStateToProps = function (state) {
return {
activeTabIdx: state.activeTabIdx
// And whatever else you have...
}
}
const mapDispatchToProps = function (dispatch) {
return {
activateTab: function (idx) {
dispatch({
action: 'ACTIVATE_TAB_IDX',
idx: idx
}) // You probably want this in a separate actions/tabs.js file...
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Tabs)
views/Tabs.js
import React, { createClass } from 'react'
import Tab from 'components/Tab.js'
const { number, func } = React.PropTypes
// Alternatively, you can use es6 classes...
export default createClass({
propTypes: {
activeTabIdx: number.isRequired,
activateTab: func.isRequired
},
render () {
const { activeTabIdx } = this.props
const tabs = ['Tab 1', 'Tab 2', 'Tab 3']
return (
<div className='view__tabs'>
<ol className='tabs'>
{this.renderTabLinks(tabs, activeTabIdx)}
</ol>
</div>
)
},
renderTabLinks (tabs, activeTabIdx) {
return tabs.map((tab, idx) => {
return (
<Tab
onClick={this.props.activateTabIdx.bind(this, idx)}
isActive={idx === activeTabIdx}
>
{tab}
</Tab>
)
})
}
})
components/Tab.js
import React, { createClass } from 'react'
const { func, bool } = React.PropTypes
// Alternatively, you can use es6 classes...
export default createClass({
propTypes: {
children: node.isRequired,
onClick: func.isRequired,
isActive: bool.isRequired
},
handleClick (e) {
const { isActive, onClick } = this.props
e.preventDefault()
if (!isActive) {
onClick()
}
},
render () {
const { children, isActive } = this.props
const tabClass = isActive
? 'tabs__items tabs__items--active'
: 'tabs__items'
return (
<li className={tabClass}>
<a className='tabs__item-link' onClick={this.handleClick}>
{children}
</a>
</li>
)
}
That will mostly do the right thing. Keep in mind that this doesn't handle/care about tab content, and as a result, you may want to structure your view differently.
I think that this is a matter of opinion. I personally like to keep my presentational components as dumb as possible. This allows me to also write most of my presentational components as stateless functions, which are being optimized more and more in React updates. This means that if I can help it, I will prevent any presentational component from having an internal state.
In the case of your example, I don't believe that it is a presentational concern because componentShouldUpdate is a pure function of the props, which should be passed whenever this component is used. Even though this component updates the application's state, I believe that because it has no internal state, it is not necessarily behavioral.
Again, I don't think there is really a right or wrong way of doing things here. It reminds me of the discussion about whether or not Redux should handle all application state. I think if you keep the idea of making presentational components as dumb (reusable) as possible, you can figure out the correct place to put lifecycle methods in any case.
Your question is not very correct.
Simple act of using a lifecycle method doesn't define the component as a presentational or a container component.
Lifecycle methods are exactly that — the hooks for your convenience where you can do pretty much anything you want.
A container component typically does some setup that connects itself to your application's data flow in those lifecycle methods. That's what makes it a container component, not the bare fact that it uses some lifecycle methods.
Presentational components are typically dumb and stateless, therefore they typically don't need those lifecycle methods hooked into. This doesn't mean that it's always the case. A presentational component may be stateful (although this is often undesired) and a stateless component may make use of the lifecycle methods, but in a totally different fashion than a container component would. It might add some event listeners to the document or adjust the cursor position of an input in a totally stateless way.
And perhaps you're mixing up container components and stateful components. Those are different things, and while container components are stateful, stateful components don't necessarily acts as container components.

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()).

Updating state in more than one component at a time

I have a listview component which consists of a number of child listitem components.
Each child listitem have a showSubMenu boolean state, which display a few extra buttons next to the list item.
This state should update in response to a user event, say, a click on the component DOM node.
childcomponent:
_handleClick() {
... mutate state
this.props.onClick() // call the onClick handler provided by the parent to update the state in parent
}
However, it feels somewhat wrong to update state like, as it mutates state in different places.
The other way i figured i could accomplish it was to call the this.props.onClick directly, and move the child state into the parent as a prop instead, and then do change the state there, and trickle it down as props.
Which, if any, of these approaches is idiomatic or preferable?
First of all, I think that the question's title doesn't describe very well what's your doubt. Is more an issue about where the state should go.
The theory of React says that you should put your state in the higher component that you can find for being the single source of truth for a set of components.
For each piece of state in your application:
Identify every component that renders something based on that state.
Find a common owner component (a single component above all the
components that need the state in the hierarchy).
Either the common
owner or another component higher up in the hierarchy should own the
state.
If you can't find a component where it makes sense to own the
state, create a new component simply for holding the state and add it
somewhere in the hierarchy above the common owner component.
However, a Software Engineer at Facebook said:
We started with large top level components which pull all the data
needed for their children, and pass it down through props. This leads
to a lot of cruft and irrelevant code in the intermediate components.
What we settled on, for the most part, is components declaring and
fetching the data they need themselves...
Sure, is talking about data fetched from stores but what im traying to say is that in some cases the theory is not the best option.
In this case i would say that the showSubMenu state only have sense for the list item to show a couple of buttons so its a good option put that state in the child component. I say is a good option because is a simple solution for a simple problem, the other option that you propose means having something like this:
var GroceryList = React.createClass({
handleClick: function(i) {
console.log('You clicked: ' + this.props.items[i]);
},
render: function() {
return (
<div>
{this.props.items.map(function(item, i) {
return (
<div onClick={this.handleClick.bind(this, i)} key={i}>{item} </div>
);
}, this)}
</div>
);
}
});
If, in a future, the list view has to get acknowledge of that state to show something for example, the state should be in the parent component.
However, i think it's a thin line and you can do wathever makes sense in your specific case, I have a very similar case in my app and it's a simple case so i put the state in the child. Tomorrow maybe i must change it and put the state in his parent.
With many components depending on same state and its mutation you will encounter two issues.
They are placed in component tree so far away that your state will have to be stored in a parent component very high up in the render tree.
Placing the state very high far away from children components you will have to pass them down through many components that should not be aware of this state.
THERE ARE TWO SOLUTIONS FOR THIS ISSUE!
Use React.createContext and user context provider to pass the data to child elements.
Use redux, and react-redux libraries to save your state in store and connect it to different components in your app. For your information react-redux library uses React.createContext methods under the hood.
EXAMPLES:
Create Context
const ThemeContext = React.createContext('light');
class App extends React.Component {
render() {
// Use a Provider to pass the current theme to the tree below.
// Any component can read it, no matter how deep it is.
// In this example, we're passing "dark" as the current value.
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
class ThemedButton extends React.Component {
// Assign a contextType to read the current theme context.
// React will find the closest theme Provider above and use its value.
// In this example, the current theme is "dark".
static contextType = ThemeContext;
render() {
return <Button theme={this.context} />;
}
}
}
// A component in the middle doesn't have to
// pass the theme down explicitly anymore.
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
class ThemedButton extends React.Component {
// Assign a contextType to read the current theme context.
// React will find the closest theme Provider above and use its value.
// In this example, the current theme is "dark".
static contextType = ThemeContext;
render() {
return <Button theme={this.context} />;
}
}
REDUX AND REACT-REDUX
import { connect } from 'react-redux'
const App = props => {
return <div>{props.user}</div>
}
const mapStateToProps = state => {
return state
}
export default connect(mapStateToProps)(App)
For more information about redux and react-redux check out this link:
https://redux.js.org/recipes/writing-tests#connected-components

Resources