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).
Related
My components are structured looks like this. Each component is it's own functional component in its own file - this is just a schematic of how they interract.
<FirstLevelComponent>
// propValue is declared on this level with useState hook.
<SecondLevelComponent someProp={propValue}>
<ChildComponent1></ChildComponent1>
<ChildComponent2 someProp={propValue}></ChildComponent2>
<ChildComponent3></ChildComponent3>
</SecondLevelComponent>
</FirstLevelComponent>
someProp is declared in state in the FirstLevelComponent and is passed along to the SecondLevelComponent and when it changes, it triggers the re-render of the entire SecondLevelComponent. But the only dependency on that prop is in one of the children. The rest of the children are unaffected.
What would be the best way to isolate that behavior and only limit scope of re-rendering to the single child that depends on that prop?
Some constraints:
This is a huge production application so something like Just add redux would not be an easy solution.
Refactoring SecondLevelComponent will be a challenge (1500 lines of code), while I am open to such opportunity`, I am looking for the way to achieve this assuming it's not a hello world project. Solutions that are easy and ideal for application in its early stages are quite a rework when we are dealing with legacy code.
and only limit scope of re-rendering to the single child that depends on that prop?
What you're looking to accomplish goes against the nature of React. If a parent component has state, and that state changes, React will react and rerender the component.
Stopping the parent component from rerendering when its state changes is an anti pattern (as opposed to simply stopping its children from rerendering with useMemo).
The way I see it, you have the following options:
Add state to the child component instead. If it's the only component reliant on this state, it should be a simple refactor.
Replace useState with useRef in the parent component. Pass this to the child for creating initial state.
Option two could lead to other implications if anything else in the app is dependant on this piece of state.
Now, if you just want to keep the extra children from rerendering, which isn't exactly what I quoted above from the question, you could just simply use useMemo (and useCallback if passing a function as a prop)..
I saw the same problem, also big application
Option 1
You can take advantage of render from React composition if you can move propValue state and ChildComponent2 component into SecondLevelComponent
Explanation: the states in SecondLevelComponent wont rerender his childs like ChildComponent1 and ChildComponent3
Option 2
You can memoize you components using React.useMemo not React.memo because you are not passing propValue in ChildComponent1 and ChildComponent3 like this:
// How to memoize
const ChildComponent1Memoized = React.useMemo(()=><ChildComponent1/>,[propValue])
// In this case ChildComponent1Memoized will not rerender again
<SecondLevelComponent someProp={propValue}>
{ChildComponent1Memoized} // <---
<ChildComponent2 someProp={propValue}></ChildComponent2>
<ChildComponent3></ChildComponent3>
</SecondLevelComponent>
Extra
if you are passing functions as props be sure you are using useCallbak in every function, because they are rebuilding in every render, that cause Memo dosent work
https://codesandbox.io/s/cocky-silence-486sh?file=/src/App.js
In the above example, we see that calling the ChildWrapper as ChildWrapper() vs <ChildWrapper/> causes some differences.
The function method behaves as intended, so when I increment the counter, the reference stays the same, however using the component call, it actually remounts.
I was wondering if my intuition is correct, that the references are changing, and more important why are they changing. And to add on to that, which should be the preferred method for rendering functions that returns jsx, <Component/> or { Component() }.
When you do <ChildWrapper/> it is translated to React.createElement(ChildWrapper, {}, null) which means you ask react to create an element for you.
When you do ChildWrapper() you are just calling a function, react does not know about this and cannot know about it, so you won't have any of react's features (hooks).
With your example, with logging the two results in the console you will see this, and you can notice that the difference is noticeable:
When calling the function, it will return a <div>, here, jsx will transpiles it to an object with type div.
When calling it via jsx from the start, it will return an object with type ChildWrapper.
PS: you are creating the ChildWrapper inside a component (during render) So it will have a new value each time your Parent component renders, and react after rendering and going into reconcialiation, it will remove the whole previous tree and create a new one, because the type changed.
Let's sum up all what I've wrote:
The child "behaving correctly for you" is the function call, because you basically call it and it returns a div, react after render and during reconciliation, finds a div and then just updates it if there is a change. So the mount useEffect(() => ..., []) will not be executed.
Why the child created with jsx isn't behaving correclty ? because you are dynamically creating its type, so at each render, it will create a new ChildWrapper Type, and react during reconciliation will remove it entirely because its type changed, and thus, the mount useEffect(() => ..., []) will be executed each time, because every time we mount a new element.
Read more about reconciliation in this link
From the official documentation :
Each JSX element is just syntactic sugar for calling
React.createElement(component, props, ...children). So, anything you
can do with JSX can also be done with just plain JavaScript.
Internally, the code generated looks like this: React.createElement(Component, { props }, children),
The createElement function will then register and render the component you gave to it, bind it with the shadow DOM and update it whenever it is necessary. It will also pass its props and children arguments.
But if you call your component directly, none of that happens, your component will not be correctly rendered and updated by react because it lacks the core features given by createElement.
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.
I don't feel right to store a function in a reactjs state, but not sure any disadvantage? Could anyone help analyze it?
view1: function() {
return <div>view1</view>
}
view2: fucntion() {
return <div>view2</view>
}
goView1: function() {
this.setState({contentView: view1})
}
goView2: function() {
this.setState({contentView: view2})
}
render: function () {
return(
{this.state.contentView()}
)
}
There're a few common gotchas about state in react:
Don't mutate state directly, use setState instead
Don't use state when not referenced in render, use instance variable instead.
Well, it seems fine to use state as function, since it's used in your render function to return what to render? Alright, it might not be an anti-pattern, but a better place to put those functions might be in your props, aka render prop, a few reasons:
Think state as your component's data model.
Data are not stale, it changes over time, it could be results from async operations, ui states after user interactions, controlled forms values etc.
Just like redux stores your app's global state, react state should store your component's local state that changes over time, while functions most of time, does nothing more than deferring execution.
Render props is different, it offers IoC (Inversion of Control) for free.
Props most of time similar to local state, should be concerned with UI rendering. However, unlike state owned/managed by component itself, props are passed from parents/clients of the component.
Event handlers for example, event target (usually DOM elements like button, input etc) doesn't need to know how to handle events when creating the component, instead it yields control back to component client/consumer who might need to use setState in its handleEventName handler to update certain container's state shared across children.
Some library (e.g., react router4) also shifted from HOC to render props (e.g., react 16.3 new context API - provider/consumer pair) in favor of its flexibility and explicitness. Instead of composing HOCs before runtime for static version of final enhanced component, you can declare what to render using render props and composing them at runtime in render functions. Thus, you get a clear separation between data (state/props) provider and consumer (where render props are passed from).
Regarding any performance (re-render) concerns, i think it's totally fine to use state as function, since most performance tricks are done by using shouldComponentUpdate hook with shallow compare state/props references (e.g. React.PureComponent, Redux Connect etc), so it's where immutability and memoization comes into play.
If that the flux store is a singleton that maintains the state of the data why do the components use setState and not setProps when accessing the stores? Wouldn't it just mean that I started saving the application state in two (or more) places?
Both the Flux / React documentation and Examples seem to point to setState as the preferred solution, but I've had an interesting conversation with a few colleagues at work and wondered if anyone else came across this
Edit:
You can see what I'm talking about in this url:
https://github.com/facebook/flux/blob/master/examples/flux-chat/js/components/ThreadSection.react.js
Notice how ThreadSection is a child component, that is fetching data directly from a store and using it as a state.
If you follow the React "way" I would have expected the state to be managed by the store - not a child component.
The solution we thought of is to fetch all stores in the top level component (as props) and pass them down to the child components as needed. But that gets rather ugly rather quickly.
We do that because setProps does not work on child components
Understand that you should have 2 kinds of components. Stateful components and view components.
Stateful components can have 3 kinds of states: initial state, user input state, and data store state.
Stateful components are like small entry points in the "widget" that you're assembling. There is no single application-wide entry point anymore for downstream dependency or data injection, because all of these widgets have their own isolated lifecycles. That's why they themselves need to access & listen to stores.
Besides behavorial properties, stateful components do not receive actual data via upstream properties.
Stateful components manage their own state and pass it to their children to render through downstream properties.
Stateful components do not normally render html DOM elements themselves directly. They're more like the controllers in MVC, and use other dumber components, the ones like views in MVC, to actually render DOM elements.
Dumber components are like views so they only contain logic to render DOM elements. Think of them as handlebars.js templates that only receive properties, and simply render those into DOM elements possibly with loops etc. They are stateless renderers.
Hope this answers your question.
According to formal documentation, a store should update the parent component's state, and pass it down via his children props:
When it receives the event from the store, it first requests the new data it needs via the stores' public getter methods. It then calls its own setState() or forceUpdate() methods, causing its render() method and the render() method of all its descendants to run.
We often pass the entire state of the store down the chain of views in a single object, allowing different descendants to use what they need. In addition to keeping the controller-like behavior at the top of the hierarchy, and thus keeping our descendant views as functionally pure as possible, passing down the entire state of the store in a single object also has the effect of reducing the number of props we need to manage.
(facebook flux docs - Overview)
It makes more sense to put store data in the component's state, this is because props may change by a parent component with componentWillReceiveProps. So it makes sense to update the state whenever:
the store's change event is fired and
whenever the props change (putting derivative data related only to the component itself to the state)
Below is a sample component that updates listening to a reflux store and also on props change. I rarely use this.props in the render function, instead I amend them (create derivative data that is only used within the component itself) as new props come in. I constantly run into this pattern so might as well write this down:
var SampleComponent = React.createClass({
mixins: [Reflux.ListenerMixin],
// reusable helper function to build state object
buildStateFromProps: function(props) {
return {
actualHeight: props.height + 20
}
},
// default props if no such was set by a parent component
getDefaultProps: function() {
return {
height: 100
};
},
// initial state with all value set to something default
// even using buildStateFromProps with default props
getInitialState: function() {
// this.props is built before this.state
var state = buildStateFromProps(this.props);
// append default data from store
state.text = '';
},
// happens when the parent component send different
// props data
componentWillReceiveProps: function(nextProps) {
// building derivative data from new props
// reusing buildStateFromProps
this.setState(buildStateFromProps(nextProps));
},
// setting up store to be used by the component
componentDidMount: function() {
// this.listenTo is a helper function ListenerMixin
this.listenTo(sampleStore, sampleUpdated);
},
// is called from the sampleStore update
sampleUpdated: function(sampleData) {
this.setState({
text: sampleData.text
});
},
render: function() {
return (
// ...
// using this.state.text from store updates and
// this.state.height from prop updates
);
}
});
The reason I send props data to state is to avoid cluttering up the render function. Otherwise the render function will contain a lot of code that is not really related to "rendering" the component. Furthermore if this derivative data is used in other parts of the application then it is easy to pull it out from the component and put it into the store.
Hope this helps.
A valid answer to this question sits hidden in the comments to a previous answer:
#idolize you can also pass stores in using React contexts (a hidden,
not yet officially documented feature). It's really nice because you
don't have to do all that passing down the hierarchy. There are a few
articles about contexts, search for it online! – Andy Jul 17 '15 at
18:41