Use `requestAnimationFrame` in redux for multiple components - reactjs

I'm building a web application for paper trading, so i have a lot of charts that rendered using canvas, and these components basically takes a same state time from my redux store so that they can render based on data between time - 1day and say time
I sort of understand how requestAnimationFrame works, but not sure if I could just treat time as a normal redux state, have requestAnimationFrame to dispatch action to increment it, and different components will just repaint properly?
class App extends React.Component {
loop(time) {
store.dispatch({
type: TICK,
timeDelta = time - this.lastTime;
});
this.lastTime = time;
this.id = requestAnimationFrame(this.loop.bind(this));
};
componentDidMount() {
this.lastTime = 0;
requestAnimationFrame(this.loop.bind(this));
}
componentDidMount(){
cancelAnimationFrame(this.id);
}
}
const timeReducer = (state, action) => {
switch(action.type) {
case TICK:
return {...state, currentTime: currentTime + action.timeDelta }
default:
return state;
}
}
const mapStateToProps = (state) => ({
data : state.data.slice(state.currentTime - 1d, state.currentTime)
});
const Chart = ({data}) => {
// this.canvas draws based on data prop
}
const ChartContainer = connect(mapStateToProps, null)(Chart);

In my opinion, your assessment is correct. You should be treating "now" as a snapshot of time in your store so that other components have a single source of truth to render based on.
The alternative approach is using the actual time in your components. The problem with this is that there is no way for the components to synchronize and confirm that they are all using the same time. Redux takes care of this for you, as each component will listen for tick dispatches and update accordingly.
Make sure that you get the time once, in one line of code, for all of your components. Once you know the time according to your application, then you should tell everything about it via dispatch.
You also want to make sure you have impure functions (such as "Get me the current timestamp") in your action creators and not your reducers.

Related

What is the meaning of actions and action types in React-redux?

Now if i want to change value in store i should do following steps:
Go to constants/actionTypes file, create a line with action type
Go to actions and create action function
In each component where i use it i should create a function for mapDispatchToProps
In reducer i should write a logic of changing
Whats the point of such complexity?
Will it be wrong if i will do just one file with actions which will change the state? For example:
// actions.js
export const setCategories = (payload, setState, currentState) => setState({ categories: payload })
export const addCategory = (payload, setState, currentState) => setState({ categories: [...currentState.category, payload] })
To make it work i can create just couple of universal functions for all projects
1) getActions, which authomaticly collects all exports from actions.js and provide them to mapDispatchToProps, so in all components we could just write
const mapDispatchToProps = getActions
code of it can be something like
// actionsDispatcher.js
import * as actions from 'actions'
const getActions = (dispatch, ownProps) => {
Object.keys(actions).forEach(actionName => {
const fn = actions[actionName]
actions[actionName] = payload => dispatch({ action: fn, payload, type: _.toSnakeCase(actionName) })
}
return actions
}
which means we pass to dispatch the function of action from actions.js
2) setState which will work similary to react function, but for redux state
then in reducer function we just right
function rootReducer(state = initialState, action) {
if (action.action) {
action.action(action.payload, setState, state)
}
// here we make it extandable for ordinary way if required
if (action.type === '...') {
// ...
}
}
and nothing else...
So the question is whats wrong in such approach that will require for coder just write a function in one file 'actions.js' and call it from any component as props.someActionName(someParams) instead of changing 4 differents files?
Thank you
Redux is supposed to make complex requirements easier to implement but if you have simple requirements then it makes implementing these requirements more complicated.
The motivation mentions CQRS(Command Query Responsibility Segregation) that separates how you read from store (in redux with selectors and I'm a big fan of reselect) with how you write to it (with action and reducers).
The actions and reducers are the command (write) part of CQRS and is event sourcing, redux is sometimes referred to as an event store. This enables you to add or remove handlers (reducers or middle ware) for your events (actions) that can update the store, dispatch other events (=actions), do asynchronous stuff, write to local storage.
If you need to do all these things in one function (async fetch, write to local storage, call other functions (dispatch other actions),...) then that function becomes unmanageable.
Even if the function only calls other functions then it still needs to know the entire process of certain action. But if (for example) you had a local storage middleware that would write to storage on certain actions then no other code needs to know how or when it's called. So when logic of writing to local storage changes it is limited to the local storage middle ware.
This is the advantage of handlers (reducers, middleware) listening to events (actions), the handler only needs to know about a small portion of the process, not the entire process.
With event resourcing we also know why the state has a certain value instead of only knowing what the state is, the article states:
However there are times when we don't just want to see where we are, we also want to know how we got there.
Another big advantage of an event store is that you can re create the data by playing back the events. All this is excellently done with redux def tools.
Here is a great book on React with Redux.
Conventional-redux is a:
Library for small and medium react applications, it wraps the react-redux and provides API based on convention over configuration pattern without breaking redux compatibility.
You simply define an interactor:
class CounterInteractor {
// initial state
defaultState() {
return 0;
}
// actions:
doubleAsync() {
setTimeout(() => { this.dispatch('counter:double') }, 500)
}
// reduce methods:
onIncrement() {
return this.state + 1;
}
onDouble() {
return this.state * 2;
}
}
And dispatch actions to that interactor from your connected component component. That's it!

componentWillReceiveProps not called after redux dispatch

I'm building a react native app and using redux to handle the state. I am running into a situation where one of my containers is not updating immediately when the redux state is changed.
Container:
...
class ContainerClass extends Component<Props, State> {
...
componentWillReceiveProps(nextProps: Object) {
console.log('WILL RECEIVE PROPS:', nextProps);
}
...
render() {
const { data } = this.props;
return <SubComponent data={data} />
}
}
const mapStateToProps = (state) => ({
data: state.data
};
export default connect(mapStateToProps)(ContainerClass);
Reducer:
...
export default function reducer(state = initalState, action) => {
switch(action.type) {
case getType(actions.actionOne):
console.log('SETTING THE STATE');
return { ...state, data: action.payload };
...
...
...
In a different random component, I am dispatching a call with the actionOne action, which I confirm prints out the relevant console.log. However, the console.log in the componentWillReceiveProps in the container is not printed.
The component that dispatches the call is a modal that has appeared over the Container, and closes automatically after the call is dispatched and the state is updated. What is weird is that although the Container isn't updated immediately, if I navigate to a different page and then back to the Container page, the state is in fact updated.
EDIT: Initial state is:
const initialState: Store = {
data: []
}
And the way I dispatch is in a different component which gets called as a new modal (using react-native-navigation) from Container:
fnc() {
...
setData(data.concat(newDatum));
...
}
Where setData and data are the redux dispatch action and the part of the store respectively that is passed in on props from the Container (which has setData and data through mapStateToProps shown above and a mapDispatchToProps which I didn't show).
I solved my problem by updating from react-native v0.56 to v0.57. Apparently there was a problem with react-redux v6 working properly in the react-native v0.56 environment.
Assuming you're using a recent version of React, componentWillReceiveProps is actually deprecated:
Using this lifecycle method often leads to bugs and inconsistencies
You can't really rely on that lifecycle hook in a number of situations. You may want to look at a slightly different approach with componentDidUpdate instead.
I think more important is to get the value after changing in state of redux rather than in which lifecycle you are getting the value . so for getting the value you can use subscribe method of redux in componentDidMount
store.subscribe( ()=> {
var updatedStoreState = store.getState();
})
I believe that getDerivedStateForProps would solve your problem.
static getDerivedStateFromProps(nextProps, prevState) {
if(nextProps.data !== prevState.data) {
//Do something
} else {
//Do something else
}
}
You would check the state from the redux against the state from your component and then act accordingly.
Also, some info from the documentation that you might consider before using this method:
1. getDerivedStateFromProps is invoked right before calling the render method, both on the initial mount and on subsequent updates.
2. This method exists for rare use cases where the state depends on changes in props over time.
3. If you need to perform a side effect (for example, data fetching or an animation) in response to a change in props, use componentDidUpdate lifecycle instead.
You can read more at: https://reactjs.org/docs/react-component.html#static-getderivedstatefromprops

When to use Dispatcher in React Application

I am facing some issue while using Dispatcher in ReactJS. So, I try to remove this dispatcher from the store and still store works well. Store properly hold my data and change event works well.
Now I am bit confusing to use dispatcher in our application.
Here is the code
MenuList is my component in which I call MenuStore.getMenuFromAPI() and after I also added onChange event of MenuStore.
class MenuList extends React.Component{
constructor(){
super();
this.state = {open:false, menuList:"",numbering:-1}
}
componentWillMount(){
var that = this;
MenuStore.getMenuFromAPI();
MenuStore.on("change", ()=> {
that.setState({menuList:MenuStore.getMenu()});
})
}
componentWillReceiveProps(nextProps){
if(nextProps.show!=this.props.show){
this.setState({open:nextProps.show});
}
}
render(){
const { classes } = this.props;
return (
<div>My MEnu</div>
)
}
}
MenuStore
class MenuStore extends EventEmitter {
constructor() {
super();
this.menu = null;
}
getMenu(){
return this.menu;
}
getMenuFromAPI(){
var that = this;
$.ajax({
type: "POST",
url: LinkConstants.GETMENU,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
data: "",
dataType :"json",
success: function(response) {
that.menu =response;
that.emit("change");
}.bind(this),
error: function(xhr, status, err) {
console.log(err);
}.bind(this)
});
}
// handleAction(action) {
// switch (action.type) {
// case ActionTypes.MENU: {
// this.getMenuFromAPI();
// break;
// }
// }
// }
}
const menuStore = new MenuStore;
//Dispatcher.register(menuStore.handleAction.bind(menuStore));
export default menuStore;
As you can see I commented out Dispatcher.register line and handleAction function.
Above code works properly fine but I wanted to know why to use Dispatcher over here ?
If I want to just store my data in the MenuStore and get it back from MenuStore on any of the component in the application. So it is necessary to use dispatchers and action or to just work with stores only.
Please clarify my doubts with proper example or case scenario (if possible) when to use dispatchers and action or when to work with stores only.
In your example your are not using Redux at all, you've just created a class that is used as a simple storage for the fetched data but your are not using any of Redux capabilities.
Redux is all about one store which is just a plain object which represents your application state tree. In order to change this state you dispatch actions. Actions are just simple objects which describe what happened. Each action has a type field which describes the action. Actions are treated by reducers which are functions that gets the current state and the dispatched action and decide on the next state of the application. This is Redux in few sentences.
Redux store has a method named dispatch which is used to dispatch actions. As mentioned in Redux documentation, this is the only way to trigger a state change.
Lets say we have a TODO list application. Our store may be represented as an array of strings (todo items).
To add a new item to the list we will define a simple action:
const addItemAction = (item = '') => ({
type: 'ADD_ITEM',
data: item,
});
Dispatching this action can be done from within one of your component's methods which will be attached to some keyboard/mouse event:
class TodoList extends React.Component {
...
// addNewItem is called with a value from a text field
addNewItem(item) {
store.dispatch(addItemAction(item));
}
...
}
As I mentioned above, state is changed by a reducer function. The reducer decides if to change the state and how to change it. If dispatched action shouldn't change the state it can just return the received state:
function todoReducer(state = [], action) {
switch(action.type) {
case 'ADD_ITEM':
return [...state, action.data];
break;
default:
return state;
}
}
Reducer is passed to createStore method:
import { createStore } from 'redux'
const store = createStore(todoReducer);
In the TodoList component you can subscribe to the store using store.subscribe method which accepts a callback function that will be called each time the store state changes. When detecting a change you can call setState of your component to set the list on the component state and to cause the component to rerender:
class TodoList extends React.Component {
....
componentDidMount() {
this.storeSubscription = store.subscribe((state) => {
// For the example I'm just setting the state (list of todos)
// without checking if it changed or not
this.setState({
todos: state,
});
});
}
render() {
return this.state.todos.map(todo => <div>{todo}</div>);
}
....
}
This is an almost complete example of using Redux. We used action to describe an event in our application, we used the store's dispatch method to dispatch the action to the store, Redux will invoke the reducer when it gets new actions, the reducer computes the new state and our component detects the change by using the store's subscribe method.
There are more things to consider and to take care of in a more complex application. You will probably have a more complex state tree so you will need additional reducers to take care of computing the state. Additionally in some step you would need to consider working with some helpers to reduce overhead of subscribing to state change and detecting changes.
In a more complex application you would probably connect your component to the store by a binding library such as react-redux so your component will receive the relevant parts of the store by props which will save the overhead of subscribing to the store changes and deciding on when to rerender the component.
I would recommend watching "Getting started with Redux" by Dan Abramov to get some more understanding of what is Redux and how to work with it.
Getting started with Redux

Reusable component using React Redux mapStateToProps

A React component OilBarrel connected my redux store to create a container OilBarrelContainer:
// ---- component
class OilBarrel extends Component {
render() {
let data = this.props.data;
...
}
}
// ---- container
function mapStateToProps(state) {
let data = state.oilbarrel.data;
...
}
const OilBarrelContainer = connect(mapStateToProps)(OilBarrel)
// ---- reducer
const oilbarrel = (state = {}, action) => {
let data = state.data;
}
const storeFactory = (server = false, initialState = {}) => {
return applyMiddleware(...middleware(server))(createStore)(
combineReducers({oilbarrel, otherReducer1, otherReducer2}),
initialState
)
}
I find it strange that mapStateToProps() receives the top level state object (the entire state of the application), requiring me to traverse state.oilbarrel.data, when the reducer (conveniently) only receives the branch of the state that belongs to this component.
This limits the ability to reuse this container without knowing where it fits into the state hierarchy. Am I doing something wrong that my mapStateToProps() is receiving the full state?
That is the mapStateToProps behavior. You have to think redux state as a single source of truth (by the way, that is what it really is) independently of the components you have in project. There is no way out, you have to know the exactly hierarchy of you especific data in the state to pass it to your container component.
No this is intentional, because you may want to use other parts of the state inside your component. One option is to keep the selector (mapStateToProps) in a separate file from your component, which will help you reuse the selector, if you app is very large and complex you can also checkout libraries such as reselect which helps you make your selectors more efficient.
Dan Abramov offers a solution for this in his advanced redux course under Colocating Selectors with Reducers.
The idea is that for every reducer, there is a selector, and the selector is only aware of it's reducer structure. The selectors for higher level reducers, wrap the lower level reducer, with their part of the state, and so on.
The example was taken from the course's github:
In the todos reducer file:
export const getVisibleTodos = (state, filter) => {
switch (filter) {
case 'all':
return state;
case 'completed':
return state.filter(t => t.completed);
case 'active':
return state.filter(t => !t.completed);
default:
throw new Error(`Unknown filter: ${filter}.`);
}
};
In the main reducer file:
export const getVisibleTodos = (state, filter) =>
fromTodos.getVisibleTodos(state.todos, filter);
Now you can get every part of your state without knowing the structure. However, it adds a lot of boilerplate.

React - constructor() and componentDidMount

I am using Redux to create my pagination. My problem is, in constructor I ran a method that will parse the url and check if there is anything about pagination. Then it runs an action that will put the data in my store. It all runs smoothly.
Then, I have the componentDidMount method when I run another action - fetching data. And there I use those props I have previously pushed. Unfortunately, the store is at its initial state.
My (simplified) code:
class NewsList extends React.Component {
constructor(props) {
super(props);
this.profile = document.getElementById('articles').getAttribute('data-profile');
this.parseHash();
}
parseHash() {
/* Method for parsing navigation hash
---------------------------------------- */
const hash = window.location.hash.replace('#', '').split('&');
const data = {
page: 1,
offset: 0
};
// hash parsing
this.props.setPagination(data);
}
componentDidMount() {
this.loadNews();
// If I use
// setTimeout(() => { this.loadNews(); })
// it works, but I feel this is hack-ish
}
loadNews() {
this.props.update({
current: {page: this.props.NewsListReducer.current.page, offset: this.props.NewsListReducer.current.offset},
next: {page: this.props.NewsListReducer.next.page, offset: this.props.NewsListReducer.next.offset}
});
}
}
When I console.log(this.props.NewsListReducer), I am getting undefined for both current and next object, but when I use Redux DevTools, the data is there. What can I do?
It seems like there's some asynchronicity somewhere in there. You're probably using react-redux right? I think the asynchronicity comes from the connected component, as it uses setState when the store state has changed. setState schedules an asychronous state update.
Therefore this.props.NewsListReducer isn't up to date in componentDidMount().
I guess this.props.update is an action that will fetch the news, right? Why is it necessary that you provide the paging information from the component to it? E.g. with redux-thunk you can access the store state before dispatching an action. This could be your chance for reading the (up to date) paging information.
E.g.
export function fetchNews() {
return (dispatch, getState) => {
getState().NewsListReducer.current.page; // Up to date
...
fetch(...).then(() =>
dispatch({...}));
}
}
Btw. it could be a good idea to not call your state *Reducer. It's the reducer managing the state, but the reducer isn't part of the state in that manner.

Resources