Basically, I have a component called Timeperiod which is a dropdown where users will select values such as "today", "yesterday", "last week", etc.
I want to re-use the <Timeperiod> component in a lot of other components and the problem is I wouldn't be able to access which value they chose if I call it in another.
I know an alternative would be to call my other components inside of Timeperiod and passing them properties based on Timeperiod state but wouldn't that mean I need to create multiple Timeperiod like classes?
Would redux or something be a good way to handle these situations?
Here's my codesandbox: https://codesandbox.io/s/ypyoq8zoz
Right now I only have App.js calling Timeperiod, but I will have many more classes calling it too. I want to be able to access the date chosen in App.js while making the Timeperiod class re-usable.
To decide if you need Redux and how much of Redux do you need, you have to consider carefully the whole application you are developing. Redux adds some boilerplate, which might be too much for the benefits you would gain.
You can however solve your problem without Redux.
Without Redux
One of the key ideas under React is called "Lifting state up". This means that if several components need access to the same piece of data, you should definitely move the storage of that piece of data in a component that is a common ancestor of all those components (see https://reactjs.org/docs/lifting-state-up.html).
In your case, it should not be responsibility of Timeperiod to keep memory of the selected date, but of App (indeed, the closest common ancestor of all components that need access to the selected period). Timeperiod should take the selected date from app via a property, and notify App when user changes the selected date using an event (again, a prop containing a function).
class App {
constructor(props) {
super(props);
this.state = {
start: new Date(), // initial value
end: new Date(), // initial value
}
}
render() {
return (
<Timeperiod
start={this.state.start}
end={this.state.end}
onStartChange={newValue => this.setState({ start: newValue })}
onEndChange={newValue => this.setState({ end: newValue })}
/>
);
}
}
With Redux
Redux allows you to keep a global state and access it in special components called Containers. You can put how many containers you want, in any point of the document tree. This seems great, but has several drawbacks:
Too many container components degrade performance
Having full access to whole state in any point of the tree could create problems if you are not super careful. A component could modify some data it should not be allowed to access, and so on.
Redux introduces some boilerplate that might be too much if the application is simple
For any update you have to define some actions capable of handling it, create a reducer to produce the next state from the previous state and the action, and connect together all these pieces
Conclusion
It really depends on your application whether Redux is good or bad, but if your component hierarchy is not too deep and your app not too complex, vanilla way could be better
So, to follow up our comments. Here's how you could approach it:
The Timeperiod component expects a getDate function that expects a title argument. When you render the Timerperiod component, each time it has a separate state and props.
Check out how I rendered more than one(in the app.js), to show that in action.
Using redux what you could do is have a within your state a timePeriod sub state handled by a dedicated reducer which stores the user's choice.
Then each of your TimePeriod component will be hooked to this state using something like
const ConnectedTimePeriod = connect(
state => state.timePeriod,
{
onTimePeriodChoose (timePeriod) {
return {
type: "CHOOSE_TIME_PERIOD",
timePeriod
}
}
}
)(TimePeriod);
Which hydrates your component with the global shared state and provides a function as a prop to update the time period.
The ConnectedTimePeriod can then be used everywhere and will share the global state.
I would suggest you take a look at the react-redux docs for example and details.
Related
I'm working with react-redux in a current project and on my search for react wisdom I came across a 'convention' I don't understand.
Other programmers tend to put a prop to state right in the constructor. Why is that?
I've never seen it in the official documentation and from there I learned that these two are two different things to hold and share data.
Is there any use in it or mybe just a personal preference?
Thanks.
It sounds like a pattern for when you need some initial value from outside the component, but then want to either ignore, or not immediately affect, the outside (by for example dispatching Redux actions on each value change).
For example, maybe you have a form that should be prefilled with some values that the component gets from the outside (Redux state) through mapStateToProps or similar. You could let every form fields onChange dispatch an action that changes the Redux state, causes the incoming props to change, and then re-render the form with the new value. Or maybe you find that to be overkill. Maybe you're satisfied with keeping the changing form data in the component internal state until the user actually submits the form, and only then dispatch an action that will post the form data, change the Redux state and pass new props (like success status) down to the form component.
Yes, it is possible, especially when you want to keep things simple by not using container components or flux / redux store to manage application's state.
A component can manage its own state, and the initialState will be assigned as the props passed from its parent component.
Consider the following example:
class TodoList extends React.Component {
constructor(props) {
// Assign todos property to state so that the TodoList component
// can self-manage this value without interacting with outside components.
this.setState({ todos: props.todos });
}
...
addTodo(todoDescription) {
this.setState({ todos: this.state.todos.concat[todoDescription] });
}
}
However, I still do recommend to separate the view components and data manipulating components when your applications is complex.
I have been using Redux for a few weeks now and I am very happy with it and I am getting used to a Redux way. I am using it with React. Still plenty to learn as both things are new to me.
I have a one problem - maybe I am doing something wrong ... Let me show you:
I have a component structure that looks like this:
App //root of the application aka smart component
CampaignTable
CampaignHeaderRow
CampaignHeader
CampaignDataRow
CampaignData
The App component is initialized as(only related code):
import * as DashboardActions from '../actions.js'
function select(state){
return {
campaigns: state.campaigns, // array of campaign objects, has name, id, time created etc
order: state.order // sort format "byWhichField"
// will affect the way how campaigns are displayed
}
}
function mapDispatchToProps(dispatch){
return bindActionCreators(DashboardActions, dispatch)
}
export default connect(select, mapDispatchToProps)(App);
App has now access to state and all actions as props.
The problem I see with it right now is:
I would like CampaignHeader to fire the action that will change the state.order state. Let say I will make <th>Text</th> inside CampaignHeader clickable. This will fire the action to change state.order which will in turn affect campaigns order on a next rerender.
So I have my action available inside App props. To pass it down to
CampaignHeader I would have to:
pass it down to CampaignHeader as props
assign it to variable inside CampaignHeader and pass it down as props to CampaignHeaderRow
assign it to variable inside CampaignHeaderRow and pass it down as props to CampaignHeader
assign it to variable inside CampaignHeader and fire the action inside onClick event....
This is a lot of boilerplate, assignments and bag passing! Just to get action fired.
All the components along the way are aware of this action.
When I decided to implement this feature I have opened CampaignHeader component file. I have added the logic and called the action, I have added the action to action file. All I needed is to get a props set. CampaignHeader component doesn't hold a reference to its parent so I didn't know straight away where should this props be injected from(in this example is obvious but I hope you get a point).
What if I will have even deeper component structure?
Is this approach correct?
Could I tackle this problem differently?
UPDATE:
As #Errorpro suggested will it be ok to connect single action and state.order to CampaignHeader?
Worried about: If I will do it once I will be doing it all the time.
There's a discussion in the issue-section of the Redux github repo about wether it's okay to use multiple connects or if everything should be passed down from the top through props, and in there Dan Abramov (the creator of Redux say's:
[...]
Nobody advocates a single connect.
[...]
The "single" only refers to small apps like the one we create in the
example. Please feel free to amend the docs to better clarify this. I
am now busy with other projects so please don't expect this issue to
get any movement unless somebody makes a PR. You can do it too.
The comment probably makes more sense in context though so check out the entire issue thread https://github.com/rackt/redux/issues/419#issuecomment-140782462
If you use redux you should know about dumb and smart component. So we use this sctructure:
component
index.js
Component.js
ComponentContainer.js
Dumb component just get props and render it. More interesting in smart component. Here it is:
export default compose(
relay({
fragments: {
viewer: () => Relay.QL`
fragment on Viewer {
any data from relay
}
`,
},
}),
connect(
null,
{
onCreate: createUserAction,
},
(stateProps, actionProps, parentProps) => ({
...parentProps,
onCreate={() => actionProps.onCreate('user')},
})
),
)(Component);
So, parentProps and onCreate function will be in dumb component's props. There you can use this.props.onCreate and invoke it or pass it farther.
Passing the actions - like any other props - from parent to child to grandchild etc is the idiomatic React way. In my opinion your approach is correct; even if it feels wrong.
But there are a couple of alternatives.
There is a feature in React called context. Context permits the passing of fields from a higher order component to a lower order component whilst skipping the middlemen. However, it's an experimental feature so I would recommend avoiding it for now. https://facebook.github.io/react/docs/context.html
Additionally, there is a Redux specific way where you can make any lower order node of your choosing a "smart component" (in the Redux sense). That is, you wrap your class export in the connect function to plug it directly to the store, in the exact same way you do for the Root node.
Personally I tend to stick to the top-down way. There may be a fair bit of boilerplate involved but at least it means your application is easy to reason about.
I've created a small ReactJS dashboard with the help of SocketIO for live updates. Even though I have the dashboard updating, it bugs me that I'm not quite sure if I did it correctly.
What bugs me the most is the Props in getInitialState as anti-pattern post. I've created a dashboard that gets live updates from a server, requiring no user interaction beyond loading the page. From what I've read, this.state should contain things that will determine whether the component should be re-rendered, and this.props.... I don't know yet.
However, when you initially call React.render(<MyComponent />, ...), you can only pass props. In my case, I get all data from the server, so the initial props just end up in this.state anyway. So all of my components have something like this:
getInitialState: function() {
return {
progress: this.props.progress,
latest_update: this.props.latest_update,
nearest_center: this.props.nearest_center
}
}
Which, unless I've misinterpreted the aforementioned blog post, is an anti-pattern. But I see no other way of injecting the state into the Component, and I don't understand why it's an anti-pattern unless I relabel all of my props to prepend initial on them. If anything, I feel like that's an anti-pattern because now I have to keep track of more variables than I did before (those prepended with initial and those without).
Disclaimer: When I answered this question I was learning / trying to
implement vanilla Flux and I was a bit skeptic about it. Later on I
migrated everything to Redux. So, an advice: Just go with Redux or
MobX. Chances are you won't even need the answer to this question
anymore (except for the science).
Passing the intial state to a component as a prop is an anti-pattern because the getInitialState method is only called the first time the component renders. Meaning that, if you re-render that component passing a different value as a prop, the component will not react accordingly, because the component will keep the state from the first time it was rendered. It's very error prone.
And here is what you should do:
Try to make your components as stateless as possible. Stateless components are easier to test because they render an output based on an input. Simple like that.
But hey.. my components data change.. I can't make them stateless
Yes you can, for most of them. In order to do that, select an outer component to be the state holder. Using your example, you could create a Dashboard component that contains the data, and a Widget component that is completely stateless. The Dashboard is responsible for getting all the data and then rendering multiple Widgets that receive everything they need through props.
But my widgets have some state.. the user can configure them. How do I make them stateless?
Your Widget can expose events that, when handled, cause the state contained in Dashboard to change, causing every Widget to be rerendered. You create "events" in your Widget by having props that receive a function.
Ok, so now, Dashboard keeps the state, but how do I pass the initial state to it?
You have two options. The most recomended one, is that you make an Ajax call in the Dashboard getInitialState method to get the initial state from the server. You can also use Flux, which is a more sophisticated way for managing data. Flux is more of a pattern, rather than an implementation. You can use pure Flux with the Facebook's implementation of the Dispatcher, but you can use third-party implementations like Redux, Alt or Fluxxor.
Alternatively, you can pass this initial state as a prop to the Dashboard, explicitly declaring that this is just the initial state.. like initialData, for instance. If you choose this path, though, you can't pass a different initial state to it aftwards, because it will "remember" the state after the first render.
OBS
You are not quite right in your definitions.
State is used to store mutable data, that is, data that is going to change during the component life-cycle. Changes in the state should be made through the setState method and will cause the component to re-render.
Props are used to pass in imutable data to the components. They should not change during the component life-cycle. Components that only use props are stateless.
This is a relevant source on the "how to pass the initial state to components".
I wonder how
controlled components (e.g an input box) can be implemented, if the data of an app is build upon immutable data and the app uses "===" equality operators in shouldComponentUpdate() for fast re-rendering.
Let's assume deeply nested data and a controlled component somewhere deep in the hierarchy:
When local component state is used (setState()) as one would implement this normally, the changes are not seen by react as the component hierarchy rendering process might skip branches, because the immutable data has not changed. To avoid this, all local component state has to live in the application state, and every event on the controlled component (e.g. typing in an input box) has to result in changing the immutable data.
Although this should work, is this the (only) way controlled components are implemented when immutable data is used??? Doing this all with flux (fire an action, fetch the store data...) might be a lot of overhead/round-trips for something simple as typing into an input box.
A component's state is a great place to put data you don't need to persist or access from multiple places. A common example of this, is an unsubmitted form. When the user does submit the form, you can then pass the local state upwards (callbacks in props) or horizontally (actions, event emitters, apis, etc.).
Also, about not updating, the only time calling setState won't trigger an update in the same component is if you have a overly negative shouldComponentUpdate. If you need local state, the shouldComponentUpdate should take that into account.
There is often a trade-off between placing state to a store or hold it locally. Both approaches work well with immutable collections.
The local state is handled using setState, no direct this.state access. JS strings are immutable, so nothing to care about
onChange: function(event) {
this.setState({value: event.target.value});
}
Sending to a store is almost the same:
onChange: function(event) {
var flux = this.getFlux();
flux.actions.setSomeValue(event.target.value);
}
Store gets the actions and do update/updateIn in immutable collection
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