How do you pass default props to store - react-redux

So how can I have a component that take's a few default props from a parent, but also has a store? These seems seem to conflict.
<Mycomponent foo="bar">
...this means my component has some state/prop called "foo", with a value that my store does not know about. Doesn't this break the pattern's visibility design? Do I need to pass these initial props down into my store in one of the react life-cycle methods? That seems like extra work and also fragile.
I'm also finding it a little frustrating to map my store's state into the component props object. For example I have a form with some fields and they can take defaults from a parent
<MyForm input1="red", input2="blue">
The store's initial state might look like this
{
status: valid,
modified: false,
form: {
input1: ""
input2: ""
}
}
And my local props before the component is rendered looks like this
{
input1: "red"
input2: "blue"
}
But when things get remapped in mapStateToProps...
var mapStateToProps = function(state) {
return state;
}
it ends up looking like this.
{
form: {
input1: "red"
input2: "blue"
}
status: valid,
modified: false,
input1: "red"
input2: "blue"
}
Because the key1 and key2 props outside the "form" object are from the initial component properties passed down from the parent. They are not part of the store's state object. I really want the props object to match the store, but it seems like it can't unless I start deleting stuff. It seems we are using the this.props object in two ways and it feels incorrect.
What I really want is the properties from the parent to go directly into my store and from there I want my component to render based on my store only. I feel like react-redux does not fit will into the react life-cycle, or at least I don't understand how its supposed to be used.

Checkout documentation: https://github.com/reactjs/react-redux/blob/master/docs/api.md - mapStateToProps - parameter: ownProps:
If ownProps is specified as a second argument, its value will be the props passed to your component
You have examples below API description. It might be helpful.
Moreover, I think that you make something wrong. Redux works well with internal sate of components. I use state to manipulate data, that reflects on GUI but they are irrelevant to the rest of the application (example: isVisible flag in accordion menu).
You should use containers (components, that are connected to the store) only if you have obtain some data from store, otherwise use dumb components.
Compose containers, nest them if needed, avoid retrieving big chunk of state in one container.
You should also take data, that is important for your component. If your container has to know all about entire state...well, it seems that something is wrong with the architecture of the application. I map only few properties from state directly to the props:
const mapStateToProps = (state) => {
return {
status: state.status,
field1: state.form.field1,
field2: state.form.field2
}
}
So I end up with flat map.
Notice, that redux performs shallow comparison of new props:
https://github.com/reactjs/react-redux/blob/master/docs/api.md
If true, implements shouldComponentUpdate and shallowly compares the result of mergeProps, preventing unnecessary updates, assuming that the component is a “pure” component and does not rely on any input or state other than its props and the selected Redux store’s state. Defaults to true.
And don't use redux in simple apps, it is for bigger projects.

Related

redux-form initialize FieldArray

I'm trying to create a redux form which integrates many aspects of redux-form.
To give a better introduction to the project I would like to share the main goals with you.
At the moment i've a dynamic form which can be extended with participants. Each participant has a superior. On user input (Async.Creatable React select) I request (call to API) all users based on the text. To sum up, an Autosuggestion. When receiving the users I make a second call to get the corresponding superior. Afterwards I would like to autofill the fields for the participant as well as for the superior.
Everything works quite well for the first participant + superior. Unlike the first participant the second superior is not getting autofilled.
Is it required to call the initialize action (redux-form) manual, when mutating the initalValues Property?
The function getFormData access the current state. These state is as expected and contains the following data.
These state is created with the LOAD action of redux-form
initial
:
attendees
:
Array(2) <-- Autofilled fields [{name: FirstTest, firstName: FirstTest}, {name: SecondTest, firstName: SecondTest}]
const mapStateToProps = (state) => {
const attendeesFromFormData = getFormData(state, REGISTRATION_FORM, [{}]).attendees
const attendees = attendeesFromFormData ? attendeesFromFormData : [{}]
return {
initialValues: {
attendees: attendees
}
}
}
export default connect(mapStateToProps, {searchADUser, autoFillAttendee, getADUser})(reduxForm({
form: REGISTRATION_FORM,
enableReinitialize: true
})(RegistrationForm))
Okay after a bit research and testing I find a simple solution for my problem.
Each time you add a participant you have to initialize manually the redux-form.

React - defaultProps vs ES6 default params when destructuring (performances issues)

I just came across a question about React performances when settings default values in one of my stateless functional components.
This component had a defaultProps which defined row: false, but I didn't like it because the defaultProps is at the end of the file, which actually makes it harder to see. And thus, we aren't aware of the default property. So I moved it to the function declaration directly and assigned it using ES6 default value for parameters.
const FormField = ({
row = false,
...others,
}) => {
// logic...
};
But then we argued with a coworker about this being a good idea or not. Because doing so may seem trivial, but may also have a great impact upon performances since react is not aware of the default value.
I believe in this case, it's trivial. Because it's a boolean and not an object/array and therefore won't be seen as a different value during reconciliation.
But, let's now see a more advanced use-case:
const FormField = ({
input: { name, value, ...inputRest },
label = capitalize(name),
placeholder = label,
row = false,
meta: { touched, error, warning },
...others,
}) => {
// logic...
};
Here, I base the value of placeholder from label, which itself is based on input.name. Using ES6 destructuring with default values for parameters makes the whole thing quite easy to write/understand and it works like a charm.
But is it a good idea? And if not, then how would you do it properly?
I talked to several people on Discord #reactiflux channel and actually got the answer I was looking for.
There are basically three use-case with React components, and in some of them, destructuring params will impact performances so it is important to understand what's going on under the hood.
Stateless functional component
const MyComponent = ({ name = 'John Doe', displayName = humanize(name), address = helper.getDefaultAddress() }) => {
return (
<div>{displayName}</div>
);
};
This is a stateless, functional component. There is no state, and it is functional because it is not a Class instance, but a simple function.
In this case, there is no life-cycle, you cannot have a componentWillMount or shouldComponentUpdate or constructor there. And because there is no management of the life-cycle, there is no impact on performances whatsoever. This code is perfectly valid. Some may prefer to handle the default displayName value within the function body, but in the end it doesn't really matter, it won't impact performances.
Stateless non-functional component
(Do not do this!)
class MyComponent extends React.Component {
render() {
const { name = 'John Doe', displayName = humanize(name), address = helper.getDefaultAddress() } = this.props;
return (
<div>{displayName}</div>
);
}
}
This is a stateless non-functional component. There is no state, but it is not "functional" since it is a class. And because it is a class, extending React.Component, it means you will have a life-cycle. You can have componentWillMount or shouldComponentUpdate or constructor there.
And, because it has a life-cycle, the way of writing this component is bad. But why?
Simply put, React offers a defaultProps attribute, to deal with default props values. And it is actually better to use it when dealing with non-functional components, because it will be called by all methods that rely on this.props.
The previous code snippet creates new local variables named name and displayName, but the default values are applied for this render method only!. If you want the default values to be applied for every method, such as the ones from the React life-cycle (shouldComponentUpdate, etc.) then you must use the defaultProps instead.
So, the previous code is actually a mistake that may lead to misunderstanding about the default value of name.
Here is how it should be written instead, to get the same behavior:
class MyComponent extends React.Component {
render() {
const { name, displayName = humanize(name), address } = this.props;
return (
<div>{displayName}</div>
);
}
}
MyComponent.defaultProps = {
name: 'John Doe',
address: helper.getDefaultAddress(),
};
This is better. Because name will always be John Doe if it wasn't defined. address default value was also dealt with, but not displayName... Why?
Well, I haven't found a way around that special use-case yet. Because the displayName should be based on the name property, which we cannot access (AFAIK) when defining defaultProps. The only way I see is to deal with it in the render method directly. Maybe there is a better way.
We don't have this issue with the address property since it's not based on the MyComponent properties but rely on something totally independant which doesn't need the props.
Stateful non-functional component
It works exactly the same as "Stateless non-functional component". Because there is still a life-cycle the behavior will be the same. The fact that there is an additional internal state in the component won't change anything.
I hope this helps to understand when using destructuring with components. I really like the functional way, it's much cleaner IMHO (+1 for simplicity).
You may prefer to always use defaultProps, whether working with functional or non-functional components, it's also valid. (+1 for consistency)
Just be aware of the life-cycle with non-functional components which "requires" the use of defaultProps. But in the end the choice is always yours ;)
Edit 10-2019: defaultProps will eventually be removed from React API at some point in the future, see https://stackoverflow.com/a/56443098/2391795 and https://github.com/reactjs/rfcs/pull/107 for the RFC.
One big difference between defaultProps and default function parameters is that the former will be checked against propTypes. The require-default-props rule of eslint-plugin-react explains it very well.
One advantage of defaultProps over custom default logic in your code is that defaultProps are resolved by React before the PropTypes typechecking happens, so typechecking will also apply to your defaultProps. The same also holds true for stateless functional components: default function parameters do not behave the same as defaultProps and thus using defaultProps is still preferred.
Looking at the advanced use-case you have, you're adding unnecessary properties to the component. label and placeholder are dependent on other properties being passed in and in my opinion, should not be listed as a parameter of the component itself.
If I were trying to use <FormField /> in my application and I needed to look to see what dependencies that specific component has, I would be a little bit confused as to why you're creating parameters that are based off of other parameters. I would move the label and placeholder into the function's body so it's clear they are not component dependencies but simply side effects.
As far as performance is concerned here, I'm not sure there would be a significant difference in either way. Stateless components don't really have a 'backing instance' that stateful components do, which means there isn't an in memory object keeping track of the component. I believe it's just a pure function of passing parameters in and returning the view.
On that same note.. adding the PropTypes will help with the type checking.
const FormField = ({
input: { name, value, ...inputRest },
row = false,
meta: { touched, error, warning },
...others,
}) => {
const label = capitalize(name),
const placeholder = label,
return (
// logic
);
};
FormField.propTypes = {
input: PropTypes.shape({
name: PropTypes.string.isRequired,
value: PropTypes.string,
}).isRequired,
meta: PropTypes.shape({
touched: PropTypes.bool.isRequired,
error: PropTypes.bool.isRequired,
warning: PropTypes.bool.isRequired,
}).isRequired,
row: PropTypes.bool.isRequired,
};

Updating Backbone Model Attributes Dynamically

I have a model that is part of a collection and retrieves data from the api. This model contains (among other attributes) the following attributes:
updatedDate //-> value retreived from API/DB
lastUpdateAttempt //-> value retrieved from API/DB
status //-> value NOT retrieved from API/DB, depends on values of above two attributes ("updated", "error", "out of date", etc...).
My question is, how can/when should I set the status attribute? Is there a way where I can dynamically set status when trying to retrieve the value? (i.e. modelObj.get("status") -> calls function to calculate value, returns result). Or should I call a function from the view to update this attribute on initialize, then add an event listener that does the same on change? (<-- somehow doesn't seem like the best solution)
I have a feeling I'm overthinking this and there is a really practical way of doing this, but I'm still a bit inexperienced with Backbone.
Thanks.
You can set the initial value of status, and listen for further changes to updatedDate or lastUpdateAttempt when the model is initialized.
Something like
Backbone.Model.extend({
initialize: function(){
this.updateStatus();
this.on("change:updatedDate change:lastUpdateAttempt",this.updateStatus);
},
updateStatus: function(){
// your logic
}
});
Or you can try this weird way (not at all tested, just a thought), which is somewhat like updating the status while accessing it. Might help if you want to control the frequency of status updates. (if you are likely to access the status way less than the number of changes that is likely to occur with the other two properties)
Backbone.Model.extend({
initialize: function(){
this.updateStatus();
this.on("updateStatus",this.updateStatus);
},
updateStatus: function(){
// your logic
}
});
and access the status like, model.trigger('updateStatus').get('status')

In Meteor, where do I model my business rules?

Beginner question : I've worked through the Try Meteor tutorial. I've got fields in my HTML doc, backed by helper functions that reference collections, and BOOM --> the fields are updated when the data changes in the DB.
With the "Hide completed" checkbox, I've also seen data-binding to a session variable. The state of the checkbox is stored in the Session object by an event handler and BOOM --> the list view is updated "automatically" by its helper when this value changes. It seems a little odd to be assigning to a session object in a single page application.
Through all this, my js assigns nothing in global scope, I've created no objects, and I've mostly seen just pipeline code, getting values from one spot to another. The little conditional logic is sprayed about wherever it is needed.
THE QUESTION... Now I want to construct a model of my business data in javascript, modelling my business rules, and then bind html fields to this model. For example, I want to model a user, giving it an isVeryBusy property, and a rule that sets isVeryBusy=true if noTasks > 5. I want the property and the rule to be isolated in a "pure" business object, away from helpers, events, and the meteor user object. I want these business objects available everywhere, so I could make a restriction, say, to not assign tasks to users who are very busy, enforced on the server. I might also want a display rule to only display the first 100 chars of other peoples tasks if a user isVeryBusy. Where is the right place to create this user object, and how do I bind to it from my HTML?
You can (and probably should) use any package which allows you to attach a Schema to your models.
Have a look at:
https://github.com/aldeed/meteor-collection2
https://github.com/aldeed/meteor-simple-schema
By using a schema you can define fields, which are calculated based on other fields, see the autoValue property: https://github.com/aldeed/meteor-collection2#autovalue
Then you can do something like this:
// Schema definition of User
{
...,
isVeryBusy: {
type: Boolean,
autoValue: function() {
return this.tasks.length > 5;
}
},
...
}
For all your basic questions, I can strongly recommend to read the DiscoverMeteor Book (https://www.discovermeteor.com/). You can read it in like 1-2 days and it will explain all those basic questions in a really comprehensible way.
Best Regards,
There is a very good package to implement the solution you are looking for. It is created by David Burles and it's called "meteor-collection-helper". Here it the atmosphere link:
You should check the link to see the examples presented there but according to the description you could implement some of the functionality you mentioned like this:
// Define the collections
Clients = new Mongo.Collection('clients');
Tasks = new Mongo.Collection('tasks');
// Define the Clients collection helpers
Clients.helpers({
isVeryBusy: function(){
return this.tasks.length > 5;
}
});
// Now we can call it either on the client or on the server
if (Meteor.isClient){
var client = Clients.findOne({_id: 123});
if ( client.isVeryBusy() ) runSomeCode();
}
// Of course you can use them inside a Meteor Method.
Meteor.methods({
addTaskToClient: function(id, task){
var client = Clients.findOne({_id: id});
if (!client.isVeryBusy()){
task._client = id;
Tasks.insert(task, function(err, _id){
Clients.update({_id: client._id}, { $addToSet: { tasks: _id } });
});
}
}
});
// You can also refer to other collections inside the helpers
Tasks.helpers({
client: function(){
return Clients.findOne({_id: this._client});
}
});
You can see that inside the helper the context is the document transformed with all the methods you provided. Since Collections are ussually available to both the client and the server, you can access this functionality everywhere.
I hope this helps.

Upon updating, how to compare a model instance with its former state

I'm using Sails.js v0.10.5, but this probably applies to more general MVC lifecycle logics (Ruby on Rails?).
I have two models, say Foo and Baz, linked with a one-to-one association.
Each time that data in a Foo instance changes, some heavy operations must be carried out on a Baz model instance, like the costlyRoutinemethod shown below.
// api/model/Foo.js
module.exports {
attributes: {
name: 'string',
data: 'json',
baz: {
model: 'Baz'
}
},
updateBaz: function(criteria,cb) {
Foo.findOne( criteria, function(err,foo) {
Baz.findOne( foo.baz, function(err,baz) {
baz.data = costlyRoutine( foo.data ); // or whatever
cb();
});
});
}
}
Upon updating an instance of Foo, it therefore makes sense to first test whether data has changed from old object to new. It could be that just name needs to be updated, in which case I'd like to avoid the heavy computation.
When is it best to make that check?
I'm thinking of the beforeUpdate callback, but it will require calling something like Foo.findOne(criteria) to retrieve the current data object. Inefficient? Sub-optimal?
The only optimization I could think of:
You could call costlyRoutine iff relevant fields are being updated.
Apart from that, you could try to cache Foo (using some locality of reference caching, like paging). But that depends on your use case.
Perfect optimization would be really like trying to look into the future :D
You might save a little by using the afterUpdate callback, which would have the Foo object already loaded so you can save a call.
// api/model/Foo.js
module.exports {
attributes: {
...
},
afterUpdate: function(foo,cb) {
Baz.findOne( foo.baz, function(err,baz) {
baz.data = costlyRoutine( foo.data ); // or whatever
cb();
});
}
}
Otherwise as myusuf aswered, if you only need to update based on relevant fields, then you can tap into it in the beforeUpdate callback.
Btw, instance functions should be defined inside attributes prop, lifecycle callbacks outside.

Resources