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.
Related
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>
);
}
}
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).
I've been reading React's Quick Start documentation;
Whether you declare a component as a function or a class, it must never modify its own props
This is a "pure" function, because it doesn't attempt to change its inputs, and always returns the same result for the same inputs:
function sum(a, b) {
return a + b;
}
This in an "impure" function, because it changes its own input:
https://codesandbox.io/s/9z38xv4x7r
function SayHi(props) {
props.name = "Jim"; // TypeError Cannot assign to read only property 'name' of object '#<Object>'
return <h1>Hi {props.name}!</h1>;
}
Why are React props read-only?
A component should manage its own state, but it should not manage its own props. props is essentially "state that is managed by the component owner." That's why props are immutable.
React docs also recommends to treat state as if it's immutable.
That is because by manipulating this.state directly you are circumventing React’s state management, which can be potentially dangerous as calling setState() afterwards may replace the mutation you made.
You may think of React component as a function of its props and state. As you advance through the docs, you'll find that this is the case, as most functions in the React component life cycle have signatures of the form (prop, state) => { //code }.
React docs define props as any arbitrary input given to a component, and the component will render something based on the props ( and sometimes based on state too, if it is a stateful component ). So props is like something that is given to the component for say, reference. Imagine it this way: you are a component, and your parent component gives you a reference book, containing some rules on how you must behave ( a.k.a. render ). Two cases may arise:
You are dumb (stateless): You just read the book, and behave so.
You are smart (stateful): You read the book, and then note some things in your notepad, that you may view, update or delete. You may even take copy down content from the book to your notepad, and then edit the notepad.
Either way, you may not update the reference book given to you. Only the parent component can update it ( example, give you another book, or change its content ).
I don't know if this is a correct representation, but React components work in a similar way. You'll get the hang of it soon. Make sure you read Thinking in React too. Happy coding!
The props of a react component is aimed to store values and functions from its parent component. It's just the pattern, props are immutable. If you want to have a variable that would be mutable, then store it in the state of the component. States are mutable.
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.
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