My question is simple, where should I put logic similar to date formatting?
Let's say I have a Date object that I'll eventually display as 01/02/2017 to the user. Where should I do the conversion from Date to String?
in mapStateToProps in the container component
in the presentational component, as part of the view rendering
My state (in the store) obviously should be stores as a Date, since I might need to present it with different formattings in other places, so I need to conversion from Date to String.
The advantage of 1 is that it makes the presentational component as dumb as possible.
The advantage of 2 is that the rendering logic stays in the presentational component.
I'm not sure where this type of logic belongs (another example would be money formatting from Integer to String adding $ and .00).
Interesting question, I could defend both options. I would normally use a selector that lives in a file next to my reducer (if you're using "ducks" or modules for example) so that even the mapStateToProps function is dumb.
However, if there's any chance that your container or some of its children will need to perform any calculation using that prop, then they will need the Date object, not a string. Same with the number / money. If you need to summarize, aggregate or substract the quantity, the string won't work.
It is better to include this logic in componentWillReceiveProps lifecycle method. So whenever you get new props date formatting logic will be executed and update local component's state variable which in turn will be rendered. Sample code is as below:
class ABC extends React.Component{
constructor(props){
super(props);
this.state= { dateVar: this.props.dataVar }
}
componentWillReceiveProps(nextProps) {
//date formatting logic goes here
this.setState({ dateVar: <formatted-date> })
}
render() {
return(
<div>{this.state.dateVar}</div>
)
}
}
Related
I need to get Redux store data but not always and different data for each component instantiate.
How can I use useSelector with Conditional statement?
The component should get data from store only when some child components are rendered and also different data each time depending on the child component.
useSelector takes in a callback that has access to the redux state as an argument
so assuming you control whether the child component is rendered in a boolean flag called isComponentRendered, and want to select data when it is true and else nothing, you could try the following:
const data = useSelector(state => {
if(state.isComponentRendered) {
return state.data
}
return null;
})
I followed flow docs and typed redux action creators using union (https://flow.org/en/docs/react/redux/#toc-typing-redux-actions)
so I have a file with ALL the actions gathered into 1 union like in example:
type Action =
| { type: "FOO", foo: number }
| { type: "BAR", bar: boolean }
| { type: "BAZ", baz: string };
Action type is imported in my reducers and used as in exxample from docs:
function reducer(state: State, action: Action): State {
switch (action.type) {
case "FOO": return { ...state, value: action.foo };
case "BAR": return { ...state, value: action.bar };
default:
(action: empty);
return state;
}
}
The problem:
As I mentioned I gathered ALL the actions in one file - currently ~600 actions in one union. I noticed that lately flow server takes crazy time to start (100+ seconds), rechecking flow is also a pain if change is related to reducer. According to flow logs, files that contain reducers are marked as "Slow MERGE" - 15 to 45s.
After experimenting, I noticed that changing my Action type to any cuts the time from 100s to 9s.
The question:
can this be related to huge Action union?
should I split it into a few smaller types which will contain only actions to import in particular reducer or this is a wrong way to fix my issue?
It's probably more likely that this one action type is used across your entire app. Any time you make a change to it, Flow needs to recheck a very large number of files. One way to help mitigate this is to ensure all your union actions are in files of their own that don't import other files. Flow can get slow if it has "cycles". One type imports another time which then imports the first time. This can happen if, for example, you define your reducer actions in the reducers themselves. This causes a cycle. Instead, move your action types to their own file.
Additionally, you can use flow cycle to output a dot file you can then visualize this file in something like Gephi https://gephi.org/ to detect cycles.
Is there any inconvenient at all if I design my reducers to, instead of reading only the partial state, had access to the full state tree?
So instead of writing this:
function reducer(state = {}, action) {
return {
a: doSomethingWithA(state.a, action),
b: processB(state.b, action),
c: c(state.c, action)
}
}
I destructure state inside doSomethingWithA, c or processB reducers, separately:
function reducer(state = {}, action) {
return {
a: doSomethingWithA(state, action), // calc next state based on a
b: processB(state, action), // calc next state based on b
c: c(state, action) // calc next state based on a, b and c
}
}
Would I'd be using more RAM? Is there any performance inconvenient? I understand that in javascript, a reference is always passed as parameter, that's why we should return a new object if we want to update the state or use Immutable.JS to enforce immutability, so... again, would it be of any inconvenient at all?
No, there's nothing wrong with that. Part of the reason for writing update logic as individual functions instead of separate Flux "stores" is that it gives you explicit control over chains of dependencies. If the logic for updating state.b depends on having state.a updated first, you can do that.
You may want to read through the Structuring Reducers section in the Redux docs, particularly the Beyond combineReducers topic. It discusses other various reducer structures besides the typical combineReducers approach. I also give some examples of this kind of structure in my blog post Practical Redux, Part 7: Form Change Handling, Data Editing, and Feature Reducers.
I'd like to know how to handle specific use case with redux reducer. To give an example, say I have a form with a DataGrid/Table. On Edit button click from a row, I want to open the popup with seleted row-data. After editing the data, On Popup-Submit button click, I want to update the Table/DataGrid (i.e. DataGrid will now should have the edited values).
I've written two separate Components
1. MainPage.js and its corresponding reducer MainPageReducer (Employee List)
2. PopupPage.js and its corresponding reducer PopupPageReducer (Selected Employee)
How these two reducers share the state?
You may need to read this first
http://redux.js.org/docs/basics/UsageWithReact.html
The main concept is that through the connect function, you would simply map needed properties of your state to the properties of your component i.e MapStateToProps. So in your case, imagine that your state, for contrived purposes, is structed like so:
{employees: {employees: {1: {id: 1, name: 'Foo'}}, editedEmployeeId: 1}
You could map the array of employees to an employees property for your EmployeeList component whilst also mapping a dispatch function, named editEmployee(id) to a click function on each row in the table.
You could map [the employee with the associated editedEmployeeId] to the individual employee in your employees array for your popup component
It may be efficient to just use one reducer instead of two.
Specifically, if you're making an update to an individual employee then you would call an EDIT_EMPLOYEE action and then a SAVE_EMPLOYEE action on save. After the SAVE_EMPLOYEE action, then, I assume, you'd call a post method, and then react-redux would re-render your entire list.
It could look like this:
function employees(state = {editedEmployeeId: undefined, employees = []}, action) {
switch(action.type) {
case EDIT_EMPLOYEE:
return Object.assign({}, state, {editedEmployee: action.employee_id})
case SAVE_EMPLOYEE:
return Object.assign({}, state, {employees: action.employees});
default:
return state;
}
}
There are great holes in my answer because the question you're asking might be too broad; I'm presuming you don't fully understand how the connect, subscribe, and dispatch functions work.
Like one of the comments said, reducers don't share state. They simply take the previous version of your state and return another version of it.
Anyways, hope this helps. Read the redux docs!
store.select() emits previous store state.
Is it possible to subscribe to changes from "this point forward" without getting the previous store value?
If you are not interested in the first emitted value, you should be able to use the skip operator:
store.select(...).skip(1)...
skip operators need piping now, you can use skip like this:
store.pipe(select(...), skip(1));
In terms of the 'hacky' part, it is a standard practice in ngrx to set an initial state with properties set to null. and that value gets emitted initially. so the first value you get will be null in these cases.
Alternatively you could also consider skipwhile(https://www.learnrxjs.io/learn-rxjs/operators/filtering/skipwhile) and use it like this:
store.pipe(select(...), skipWhile(val => val === undefined));
where undefined is the initial value of the property you are interested in. Rather than setting the initial value of the property to undefined, you could use null as the initial value as well, and change the above skipwhile() accordingly.
Just sharing my thoughts (and solution) after reading #Niz's answer.
This is a perfect, practical example of how to utilize the difference between null and undefined. When you initialize your state with null, you're basically saying:
I don't care about differentiating the nullable future state from the
initial one. I don't care if the user is null because he has signed
out or because he just didn't sign in
However, in some cases this could be insufficient. Think about a case when you need an asynchronous call (implemented in effects) in order to know if you have an active user session. Based on the selection result, you should determine whether to show a login modal or redirect to a content page. With initial user state set to null, you'd pop up that modal and then immediately hide it when that asynchronous call returns a session value.
With initial state set to undefined you can make that differentiation, saying:
Initially, I know nothing about my state, then it's undefined. When I know it should be empty, then I'll set it to null.
Therefor, as a practical solution, I set everything on the app's initialState to undefined. In the example above, I need to know if the login modal should be displayed after the asynchronous call resolves. skipWhile(val => val === undefined) will do the job for sure, but repeating it over and over again feels a little tedious. Plus, it's not really descriptive to our use case. I created a rxjs-custom-operators.ts with a shortened implementation:
import { Observable } from "rxjs";
import { skipWhile } from "rxjs/operators";
export const skipInitial = () => {
return <T>(source: Observable <T>): Observable<T> => {
return source.pipe(skipWhile(value => value === undefined));
};
};
Usage:
navigateOnLoad(): void {
this.store.pipe(select(selectAuthUser), skipInitial()).subscribe((authUser: CognitoUser) => {
// Navigate to login if !authUser, else navigate to content...
});
}