does lifting up state in react create a dependency on the parent - reactjs

I'm reading through the Lifting Up State guide on the React docs.
It seems to me that this refactor creates a dependency in the TemperatureInput component on its parent, whatever it is, to provide data.
What if I wanted to use the TemperatureInput component somewhere else in my app where I don't need this kind of data binding? Wouldn't that dependency on the component's parent still exist, meaning that any component containing it would need to provide that state?
This seems to violate the idea that data should be held as close to where it's used as possible. The parent doesn't necessarily need to know the value of this component, but because of the component's design, the parent is forced to define and contain that value.

It seems to me that this refactor creates a dependency in the TemperatureInput component on its parent, whatever it is, to provide data.
It does. And the reason they did this for this case is because they want anything entered in one input to be mirrored in the other. Some component is going to need to manage the state to keep the two linked, and the parent component is the most natural place to do that.
As they said, this is called "lifting state up", but another description of what they've done is change TemperatureInput to be a controlled component. Controlled components are ones that are pretty dumb on their own, just taking their instructions from a parent component. This makes them very flexible, since that parent can implement whatever logic it likes. But it does mean that the parent has to implement that.
The opposite of a controlled component is an uncontrolled component. Uncontrolled components may take some initial values from the parent, but after that they handle things themselves. This makes them less flexible, since they have to already have the functionality inside them, but it means they can usually be used with less effort.
One place you may have encountered controlled vs uncontrolled components is in the standard dom elements, such as <input>s. An input can be used in a controlled manner by passing value and onChange props, which results it being controlled by the parent component. Or, you can pass in defaultValue, and let the input handle the rest.
Both styles (controlled and uncontrolled) have their uses, and it just depends on what the needs of your app are what you should use for this component. But you're not limited to one or the other: as the <input> component demonstrates, it's possible for you to support both modes of operation in a single component. So if you have one page where the temperature inputs need to be linked, and another where they don't, you could add some extra code to TemperatureInput so it can work with both:
const scaleNames = {
c: 'Celsius',
f: 'Fahrenheit'
};
class TemperatureInput extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
temperature: props.defaultTemperature ?? ''
};
}
handleChange(e) {
if (this.props.onTemperatureChange) {
// Prop exists. We're in controlled component mode.
this.props.onTemperatureChange(e.target.value);
} else {
// Uncontrolled component
this.setState({temperature: e.target.value});
}
}
render() {
// If the prop exists, use it (ie, controlled component)
// Otherwise, use our state (uncontrolled component)
const temperature = this.props.temperature ?? this.state.temperature;
const scale = this.props.scale;
return (
<fieldset>
<legend>Enter temperature in {scaleNames[scale]}:</legend>
<input value={temperature} onChange={this.handleChange} />
</fieldset>
);
}
}

Related

Data flow in React app w/ functional components & hooks

I'm trying to wrap my head around the data flow in a React app w/ functional components & hooks.
I'm wondering:
When a data change (state change) causes a cascade of code to execute... what code (say, in each component, does and does not run... apparently there is selectivity such is "don't put that variable in the deps array if you don't want that code to run")?
How is the "family" part of the family tree determined during such a data-cascade? Does data pass to siblings? Does it only go to a child (or a parent if a function was passed down for updating the parent)?
To clarify what I have in mind, I have ended each file name with a labeling convention like so: I claim that (and request corrections!) 1 is the parent of 2; 2 is the parent of 3a (I think... can a custom hook be a "child"?), 3b, and 3c; and 3c is the parent of 4c.
Clearly parent/child data flow is a natural part of React. What about sibling to sibling? Is that where problems happen? Certainly "passing data" within a given file can be dangerous (vis-à-vis having control over if and when rendering of a piece of data happens) and apparently the solution is to "lift" the data up the tree. But even still... there is no sense in lifting data up a level (or more) if it isn't clear how it trickles back down... and what problems we should be looking our for.
index1.tsx
...
<App/>
...
App2.tsx
...
const App = () => {
...
const {varFromCustomHook} = useAppLogic(varToCustomHook);
...
<FooComponent varToFoo={varToFoo} functToFoo={functToFoo}/>;
<BarComponent/>;
...
};
...
useAppLogic3a.tsx
...
interface Props {
varToCustomHook;
};
const useAppLogic (props: Props) {
...
return {varFromCustomHook};
};
FooComponent3b.tsx
...
interface Props {
varToFoo;
functToFoo;
}
const FooComponent = (props: Props) => {
...
funcToFoo(importantData);
...
<div>{varToFoo}</div>;
...
};
BarComponent3c.tsx
...
const BarComponent = () => {
...
<FoobarComponent/>;
...
};
FoobarComponent4c.tsx
...
const FoobarComponent = () => {
...
};
A react component is a react component, whether it is a class-based component or a functional component is an implementation detail. Data flows down the react tree, parent to child, in the form of props. This is a universal truth in React.
When a data change (state change) causes a cascade of code to
execute... what code (say, in each component, does and does not run...
apparently there is selectivity such is "don't put that variable in
the deps array if you don't want that code to run")?
When state and/or props update, the functional component is rerendered. The entire function body of a functional component is technically the "render" function, so all of it is run when the component renders.
You ask specifically about hooks. Hooks are also executed each render cycle, in the order they are declared, and if there exists a dependency array, it is evaluated and if any dependency fails shallow reference equality check then the hook's callback is triggered.
How is the "family" part of the family tree determined during such a
data-cascade? Does data pass to siblings? Does it only go to a child
(or a parent if a function was passed down for updating the parent)?
The React tree is determined the same way it has almost always been determined, a root node, and children, where each child component can have further children. Data is still passed only from Parent to Child. Callbacks are still passed as props (generally) for a child component to invoke.
Comment Questions
Is useAppLogic considered a child [of App in this case], or can custom
hooks not be considered children (for whatever reason)? Assuming the
answer is yes, then couldn't useAppLogic return a value that gets
passed to its sibling, FooComponent? If yes, wouldn't this be data
flowing "horizontally" and not down? I don't know the answer... but it
seems like this kind of data-pass is possible (maybe it is an
anti-pattern, I don't know).
No, useAppLogic is a react hook and can't be a child of anything, it's a function. Only react components, HTML elements, and primitives (string, number, etc.) can be a child, rendered as JSX. Data flows only down. If data needs to be passed to siblings it needs to be lifted to at least the nearest common ancestor. If useAppLogic is in App, and FooComponent is a child of App, then any value returned by the hook can be passed as a prop to FooComponent.
What if (in the above case we have been discussing in these comments)
useAppLogic was use by both App and App's child, FooComponent? Would
this be an anti-pattern? This would apparently allow a parent and a
child to have a piece of data that was not "passed down". (To go out
on a limb... is this a window into a conversation on global
data/useReducer?). Maybe these points here in the comments would help
some people if they were in the answer.
React hooks are each their own instance. They don't share any state, or anything else for that matter. There isn't enough context to say whether or not both parent and child component using the same react hook is an anti-pattern, but I'm inclined to say no, it isn't, since any functional component can use any react hook for just about any reason. Not a window into any global data (useContext hook would be about as close as you could get to some "global" data).

How should I handle component state following single responsibility pattern

I'm new to ReactJs and trying to follow best practices. From my research, I've come across a couple of contradicting articles discussing how implementation should be.
Should state rely on the properties being passed down from a parent component? In the comparisons below, they are both following SRP, but not sure which is best. Would like your advice, Thanks!
1. -- Best Practices for Component State in React.js
First, and probably the most important of all, the state of a component should not depend on the props passed in. (see below for example of what we should not do)
class UserWidget extends React.Component {
// ...
// BAD: set this.state.fullName with values received through props
constructor (props) {
this.state = {
fullName: `${props.firstName} ${props.lastName}`
};
}
// ...
}
2. -- 7 architectural attributes of a reliable React component
Let's refactor to have one responsibility: render form fields and attach event handlers. It shouldn't know how to use storage directly.....The component receives the stored input value from a prop initialValue, and saves the input value using a prop function saveValue(newValue). These props are provided by withPersistence() HOC using props proxy technique.
class PersistentForm extends Component {
constructor(props) {
super(props);
this.state = { inputValue: props.initialValue };
}
// ...
}
3. -- In my case, I have something like the following (wondering if this is an acceptable implementation?) - Should state be handled in Tasks, or in another TasksWithPersistence type of component that sits between TasksWithData and Tasks?
export default function TasksWithData(TasksComponent) {
return class withData extends React.Component {
render() {
const tasks = TaskAPI.getTasks();
return (
<TasksComponent
tasks={tasks}
{...this.props}
/>
)
}
}
}
export default class Tasks extends React.Component {
state = {
tasks: [],
addItemInput: null
};
// ...
componentDidMount() {
this.updateComponentState({tasks: this.props.tasks});
}
componentDidUpdate() {
this.prepUIForNextAddition();
}
// ...
}
The gist of your question seems to revolve around the anti-pattern that is to take some props and duplicate it into the state. This, mutating of props, isn't the purpose of the state. Props are immutable, duping them to the state defeats this design.
The purpose of the state is to manage things that are specific to the React Component, i.e. tightly scoped to only that React component. For instance a showHide switch for something to display within the React component. Think of the state as a locally scoped variable if it helps.
Most of the time this anti-pattern of duping the props can be satisfied by a function within the React object. For example, your state.full_name variable becomes a named function, fullName, bound to the React Component. (all code examples are assuming JSX syntax)
Note: in JavaScript camelcase is the naming structure for functions and variables, I'm assuming you're coming from ruby based on the underscore naming convention. IMO it's best to stick to the convention of the language with which you're writing the code. This is why I use camelcased naming.
...
fullName() {
return this.props.firstName + " " + this.props.lastName
}
...
That function can then be called within the render of the component
# in render() portion of your React component, assuming jsx syntax
<p>Hello, {this.fullName()}</p>
Note: Remember that in ES6 you have to bind the methods in your react class in the constructor or use => syntax so that you can call them with this.
...
constructor(props) {
super(props);
this.fullName = this.fullName.bind(this);
}
...
You could also decompose the relevant parts to a new Component called FullName if it will be utilized by multiple components.
<FullName firstName={this.props.firstName} lastName={this.props.lastName} />
Technically, "the react way" is, at least in this author's opinion, to decompose this into another component for reusability. However component reuse needs to be weighed against the complexity added, i.e. don't optimize prematurely. So you may not want to take that too far at first. The times when it's necessary will emerge naturally.
A very broad generalization of React's props is that they are guaranteed, are immutable, and they flow down like a waterfall from the topmost component. If you need to update them, update them at the highest level where it makes sense.
In a soley React based approach, if you have something that a parent needs to be aware of, "lift" that part of the code up to the parent and vice versa bind it down to the child as a props, e.g. an AJAX function that calls an API. I think of it as trying to keep the components as dumb as possible.
The parent becomes the "source of truth" for the item you "lifted". The parent handles the updates, and then passes the results to the children. So in the parent, it may exist as a state variable and then get passed as props to the child object, which then passes it along as props to it's child object, etc. The children would update as the state gets changed in their parent when it propagates down through the chain as props.
If your app is React only, i.e. no stores that manage objects such as in the flux pattern or redux pattern, you may have to store things in the topmost objet's state which technically could be viewed as bad. As your system becomes more complex, this functionality would be better handled by flux or redux's parts.
Hope this helps!
There is a huge difference between example 1 & 2.
In example #1, the reason it's bad to set state from the those props in that way is that if the props change, the widget will not update. Best practices or not, that is just wrong and bad in any framework. In that particular case, there really is no point in even using the state. Props alone will suffice.
In example #2 the prop is only being used to give the state an initial value (The prop is even named initialValue), implying that further changes to the state will be controlled by the component regardless of prop changes. It does not break single responsibility principle to use props for an initial state, especially when it's explicitly use for that purpose.
I really don't see those two examples as being contradictory because they are completely different. Further, there is no rule in single responsibility principle that you can't set state from props, you just need to pay attention to the context in which you are doing it.

In what situations react creates a new instance of a component?

TL;DR: Can I trust the reconciliation algorithm not to re-instantiate my stateful component simply because the change in Virtual DOM was too complicted to keep track of it?
I believe, that React.Component instances are created and destroyed by React run-time to match the shape of Virtual DOM. As a programmer I declaratively describe the Virtual DOM, and the life cycle of instances is controlled by the React itself. I understand that a special reconciliation algorithm tries hard to reuse as much of old instances as possible when the Virtual DOM changes its shape. Also, I understand that if the only difference is in props, then an old instance simply gets its props updated and informed about it via lifecycle methods.
I also expect that for functional components which simply map props to Virtual DOM there is no point in talking about lifecycle, as it does not really matter if this is the same or not the same instance. In particular one does not have to trust the reconciliation algorithm to be very smart, as the component would look and behave the same regardless of if it is the same instance with updated props, or a fresh instance.
But can I, and should I, trust the reconciliation algorithm in case of stateful components?
Say, that I have a component which stores some data in its state and directly on its instance. Moreover, let's assume that the constructor of this component initializes both of them:
constructor(props) {
super(props);
this.state = {
value: ''
};
this.length = 0;
}
Also, assume that the state evolves in time, say, because of user actions
onChange = (e) => {
this.setState({value: e.target.value}
this.length = e.target.value.length;
}
Can I, or can I not, assume that this instance will not get killed and reconstructed on a whim of the React framework? The above component is not "functional", as it has an inner state, which as a developer I presume to be somehow "preserved" even if something complicated happens in the higher part of the components tree during reconciliation.
From the many examples in the docs and the web, I understand, that the community assumes, that the state and private properties of the instance are not reset, unless the parent explicitly changes the key property, or gets rid of the child, and instantiates a new one later on. But are these assumptions explicitly stated in the docs?
And the question in the other way round: is changing a key of a child guaranteed to make a new instance, or can react decide to reuse some old instance with a different key (thus reusing its state as well)? In other words: is the key trick guaranteed to work or is it just a hack?
EDIT
An example of a problematic situation: https://codesandbox.io/s/9rqrPJnLD. Here we have a stateful component:
class Stateful extends React.Component{
state = { now: new Date()};
render(){
return <div>{this.state.now.toString()}</div>
}
}
living in a hostile environment, of a parent who often changes mind about the layout, and the child is sometimes nested in a div, and sometimes not:
class App extends React.Component{
state = { div: true}
componentDidMount(){
setInterval(()=>{
this.setState(state => {
return {div: !state.div}
});
},2000)
}
render(){
return React.createElement(this.state.div ? 'div' : 'span',null,React.createElement(Stateful))
}
}
It seems that the child is re-instantiated each time the parent changes layout, and the state is not preserved when it happens. This partially answers my own question. The unanswered part being: what are the other cases which are difficult for the reconciliation algorithm.

Selective children component render

A basic question I need help here.
Whenever this.setState invoked at parent components, all the children components will be rendered. This will cause the performance issue if I have huge amount of child components.
Lets give an example,
Parent Component
handleToggleTick() {
const newObj = Object.assign({}, this.state, { iconName: ''});
this.setState({
iconName: newObj.iconName,
});
}
render() {
return(
<ChildComponentA iconName={this.state.iconName} toggleTick={() => this.handleToggleTick}></ChildComponentA>
<ChildComponentB></ChildComponentA>
<ChildComponentC></ChildComponentA>
)
}
Based on the example above, whenever handleToggleTick invoked from childcomponentA, setState invoked for new iconName. What I want is, only ChildComponentA only the one get render since props.iconName is related to it, but not for childcomponentB and childcomponentC.
I understand there is an option to check shouldComponentUpdate in childcomponent to prevent it get render. But, imagine I have over 100 of childcomponent, would it be frustrating to write over 100 times of shouldComponentUpdate method?
I need help here, please advice!
React doesn't provide any way to render children selectively. The component will either render or not. But I need to highlight a few points why this is not a problem when we use React in practice.
First of all, you don't need to manually implement shouldComponentUpdate for each component. If you don't want to rerender component if its props and state haven't changed, you can just extend from the PureComponent class instead of the Component class. Note that React.PureComponent's shouldComponentUpdate() only uses shallow comparison for state and props. But this shouldn't be a problem if you follow react best practices and avoid mutating the state.
Also, it's not practical to have more than 100 different components in one render method. React always encourages decomposing your UI into smaller components and using component composition. When we follow this approach, components will be nested inside each other in different levels instead of having a large number of components in one render method.
What I'm trying to explain is it's more practical and easy to manage when we compose our component in a nested fashion (2) rather than having lots of components inside a big container component (1).
In your example, if ChildComponentB and ChildComponentC are inside another component called ChildConatainerComponent then we only need to implement shouldComponentUpdate() for ChildConatainerComponent. Then it will automatically stop rendering any child element inside it.
render() {
return(
<ChildComponentA iconName={this.state.iconName}
toggleTick={() => this.handleToggleTick}/>
<ChildConatainerComponent/>
)
}
class ChildConatainerComponent extends PureComponent {
render() {
return (
<div>
<ChildComponentB/>
<ChildComponentC/>
</div>
);
}
}
Another very import concept to keep in mind is calling render function doesn't mean that React recreates all the DOM elements again. The render method only make changes to React virtual DOM which is an in-memory representation of DOM and it's faster than actual DOM. Then React compare versions of virtual DOM which are before the update and after the update and the actual DOM will be updated with only what has actually changed.
Another solution you could consider is moving iconName into ChildComponentA, considering this is the only component related to it.

How do I manage state on a React component that can have state changed from the parent or from events upon it?

I'm trying to make a custom checkbox component (a three-state, actually, but that's irrelevant except to say that I'm not just using an INPUT), and I'm not sure how I can make it able to change "checkedness" from clicks on itself and from a value-set coming down from the parent.
Currently, I have it working as a self-sufficient component that takes an onChange prop with the handler callback that it calls to send the value the parent component after clicks. It uses a state to store the checkedness, which is referenced by the display.
If it were merely a display of checkedness, with value being managed from outside, I'd use props, naturally. If it were only a self-sufficient checkbox component that took an initial value then only responded to clicks, I'd use state, like I am, but my problem is that I want it to be clickable to turn itself on and off, and allow the parent to turn it on and off as well.
I'm a beginner to React and the "React way of thinking" so I suspect I'm just approaching this wrong. I kind of get the impression that the proper way to do this would be for it to be a display-only component that passed clicks up to the parent to deal with, and in turn received props updates for value changes down from the parent, but that would make the component far less reusable, to my mind.
So how would I go about making a checkbox change from both internal and parent sources?
Relevant links are welcome, as well.
You may treat the checkbox as a dumb component, which means that it doesn't hold any internal states, but only receives data from outside via props and then render them. You can see the detailed definition of dumb components here.
Meanwhile, when the checkbox is clicked, such event will be handled by the component's parent, or event ancestors, this is called inverse data flow, which is described in Facebook's Thinking in React blog post.
Moreover, to decide which component should hold certain states, I find the following guidelines very useful:
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:
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.
The pseudo-code snippet:
const Checkbox = React.createClass({
// Propagate the event to parents
onClick(evt) {
if (this.props.handleClick) {
this.props.handleClick({checked: evt.target.value});
}
},
render() {
return (
this.props.checked ?
<Input onClick={this.onClick} type="checkbox" label="Checkbox" checked></Input> :
<Input onClick={this.onClick} type="checkbox" label="Checkbox"></Input>
);
}
});
const Container = React.createClass({
handleClick(evt) {
// Change the container's state, which eventually changes the checkbox's view via props.
this.setState({checked: evt.checked});
},
render() {
return (
<div><Checkbox checked={this.state.checked} handleClick={this.handleClick}></Checkbox></div>
);
}
});
You change it from only the parent.
class ParentComponent extends React.Component{
handleChildCheck(){
this.setState({
childChecked: !this.state.childChecked
})
}
render(){
return(
<ChildComponent checked={this.state.childChecked} handleCheck={this.handleChildCheck.bind(this)} />
)
}
}
Now if you wish to control the checked state from the <ChildComponent/> just call this.props.handleCheck() from the <ChildComponent/>
This way the controls will always be available within the <ChildComponent/> via this.props.handleCheck() within the <ParentComponent/> via this.handleChildCheck().

Resources