React.js/Redux: setInterval and clearInterval in Reducers - reactjs

Currently I am working on FCC Game of Life and I was able figure out how to generate the next app state (incrementally).
That being said, my next step is to figure out how to generate new app state (generation) continuously. In this case, I am trying to implement setInterval and clearInterval inside my reducer.
I'd like to have the ability to start/pause generating new generation (by toggling the state in state.start)
The thing I am having trouble of implementing it is the scope issue.
Here's part my reducer:
reducers/reducers_grid.js
export default function(state = initialState, action){
switch (action.type) {
....
case START:
let toggleStart = state.start === true ? false : true;
if (state.start === true){
console.log('START THE GAME')
const newGeneration = nextGeneration(action.payload,state)
return Object.assign({}, state, {cells: newGeneration, start: toggleStart })
}
else {
console.log('PAUSE THE GAME')
return Object.assign({}, state, {start: toggleStart })
}
return state;
}
Essentially, nextGeneration function results the new app state (generation) and updates it to via Object.assign
At first, I thought putting setInteveral inside the first if statement and clearInterval in the second if statement, but obviously that will create a scope issue.
It seems very trivial this part of logic, but I've been stuck on this awhile.

Don't do this in the reducer. Reducers should be pure functions (meaning no side-effects). Instead, you could create a component that is rendered only when the game is running (a boolean state which is set to true by the START action and false by the STOP action). Then in the component's componentDidMount function call setInterval with a function that dispatches a NEXT_GENERATION action. After that dispatch another action which adds the timer id to the store. Then, in the componentWillUnmount call clearInterval with the timer id.

Related

reducer return state twice, how to update in store?

My reducer returns state in a async way, since it will interact with another component, how to make the component re-render after reducer update state in the .then?
switch (action.type) {
case RELEASE_PAYMENT:
(new PaypalContract())
.releasePayment(action.id)
.then(() => (new PaypalContract()).getState(action.id))
.then(function(e) {
console.log("status in orderReducer:",status[e])
return{
...state,
orderStatus: status[e]
}
})
return state
You can't return the state twice from the reducer function and you can't return the updated state from the reducer function in an asynchronous way.
Returning something from the callback function of the .then() method doesn't makes it a return value of the outer function, i.e. reducer function in your case.
Reducer functions are synchronous, they take in a action and based on the action return the updated state. All of this is done synchronously.
What you are doing in your reducer function won't work as you expect it to and the return statement at the of your reducer function will always execute before asynchronous code completes. This means that your reducer function always returns the same state which means that your redux store never updates.
Solution
What you need is a way to dispatch an action that triggers the data fetching and once the data is available, another action should be dispatched that is received by the reducer function. This new action should contain the data that is fetched asynchronously, as a payload and the reducer function will just update the state appropriately using the payload associated with the action.
Following are the couple of options that allow you to do just that:
redux thunk
Redux Saga
Using the first option is easier so i would recommend to start with that but you could also explore the second option which also help with advanced use cases.
Here are couple of resources to get you started with each of the options mentioned above:
Redux Thunk Explained with Examples
Redux Saga Example
You need to pass the data to the reducer after you get the response from the API. It will be simpler to control the flow of data. You can make use of async/await in this.
async function_name() {
const res = await API_CALL_HERE
res.then( data => INVOKE_YOUR_DISPATCH_HERE(data))
.catch(err => console.log('error'));
}
In reducer
case RELEASE_PAYMENT:
return {...state, orderStauts:data[e]

So... Redux action dispatch is sync or async? (Without thunk or saga)

I am little confused about behaviour when I dispatch redux action.
Example:
onPressAdd() {
this.props.addFloor({
name: this.state.floor_name,
});
console.log(this.props.floors);
}
I am calling redux action addFloor what adds floor into array in store, then I console.log this variable and I expecting updated state ([{name:'whatever'}]) but I am getting [] (empty array)
Example 2:
async onPressAdd() {
await this.props.addFloor({
name: this.state.floor_name,
});
console.log(this.props.floors);
}
In this example I am getting perfectly fine updated store: [{name:'whatever'}]
I am reading everywhere that "Redux actions dispatch is sync if there is no thunk or saga (Direct way: dispatch action->reduce->store", but rhis is proof that dispatches are ASYNC.
So where is truth?
Dispatching by itself is 100% synchronous.
This is a tiny implementation of a Redux store:
function createStore(reducer) {
var state;
var listeners = []
function getState() {
return state
}
function subscribe(listener) {
listeners.push(listener)
return function unsubscribe() {
var index = listeners.indexOf(listener)
listeners.splice(index, 1)
}
}
function dispatch(action) {
state = reducer(state, action)
listeners.forEach(listener => listener())
}
dispatch({})
return { dispatch, subscribe, getState }
}
By the time dispatch() returns, the store has executed your reducer function, and called all the store subscriber callbacks.
It's only when you start adding middleware into the store that the dispatching process can be interrupted, because any middleware can delay, stop, or rewrite any action that was dispatched.
What you're seeing in that example is actually based on how React works. Inside of that click handler, React has not yet re-rendered and updated the props of the component, so this.props.whatever will still be the same before and after the dispatch.
The key thing is to realise that React doesn't update the props of your component until the execution of a called handler is not finished as a macrotask. Hence, your console.log after dispatch doesn't have updated props yet.
Roughly saying, doing your Example 2, you simply split it and get a new microtask with all included after await line, which will be executed after the props are updated.
That would be the same if you do Promise.resolve(this.props.addFloor(...)).then(() => console.log(this.props.floors)) without async/await.

Redux - get initial state from the backend - which is the proper way to do it?

I would like to get some variables from the backend (ie showTutorial) for the initial state in a reducer.
I was thinking about using just a call in axios from the reducer, but I am unsure if that is the best way of doing it.
This is how it is done right now:
import { UNSET_TUTORIAL, SET_FILTER_BLOCKS } from "../actions/types";
const initialState = {
showTutorial: true, // <-- instead of initializying to true, do the axios call
filterBlocks: "ALL"
};
export default (state = initialState, action) => {
switch (action.type) {
case UNSET_TUTORIAL:
return { ...state, showTutorial: false };
case SET_FILTER_BLOCKS:
return { ...state, filterBlocks: action.payload };
default:
return state;
}
};
I do not want to use redux-persist as I would like to understand first the proper way of doing it.
There is no difference between how you do this and how you update state from a backend for any other component.
You should have an action triggered on a top level app component (or any component created at application launch) that loads data from the backend and dispatches the action with the results from the backend. Your reducer then updates state based on this action.
The triggering of the action on your top level app component can be done using a lifecycle event when the component mounts, typically getDerivedStateFromProps()
The best way to do it is to call the relevant action from App.js, and render 'loading' until the data is properly fetched. Obviously, in case of error you will not continue to render components but display an error message.
Reducers should never call actions, this is breaking the redux flow.

Selector being called even when I don't mutate what its mapPropsToState

I have a React app that does some simple recording. I have a Component Recorder which connects to my redux store like this:
export default connect(
state => ({
recordings: state.recordings,
recordingSelector: selectRecordingBufferWithID(this.recordingID)
}),
dispatch =>
bindActionCreators({
startNewRecordingAction,
stopNewRecordingAction
},
dispatch
)
)(SampleRecorder);
The problem I'm having is that selectRecordingBufferWithID in my redux code is firing too often. Part of my reducer code looks like this:
function samplesReducer(state = [], action) {
switch (action.type) {
case MORE_SAMPLES:
return [...action.samples];
default:
return state
}
}
function recordingsReducer(state = [], action) {
switch (action.type) {
case NEW_RECORDING:
return newRecording(state, action.recordingID);
case STOP_RECORDING:
return stopRecording(state, action.recordingID);
default:
return state
}
}
const rootReducer = combineReducers({
samplesReducer,
recordingsReducer
})
const store = createStore(rootReducer);
export { store };
So, while I want selectRecordingBufferWithID to be utilized only when a START/STOP_RECORDING action occurs, it is called for each time MORE_SAMPLES is called.
My understanding of react-redux is that the selector is part of the mapStateToProps function that the connect function accepts. And somehow, connect cause my component to render and for its props to be updated with the mapped state from the redux store. the selectRecordingBufferWithID selector will also be called each time this happens so I can do a refined getter into the store.
So to summarize, my recordingSelector is firing more often than I expect. My only theory is that my reducers are somehow mutating the state of state.recordings each time it tries to reduce state.samples which makes react-redux render my component with it mapped to state.recording.
But otherwise, I'm stuck.
connect does not work the way you think it does. What it really does is:
Subscribe to the store. This subscription will be triggered after every dispatched action.
Execute your mapStateToProps to inject the initial set of props to your Sample Recorder component.
When any action dispatches, the subscription kicks in, and connect applies again your mapStateToProps to new global state.
If your selector returns the same props as before, it won't render your SampleRecorder again.
So the misunderstanding is that your selector shouldn't be called. But the fact is that connect needs to call your selector to decide when to re-render and when not.
The summary of this is that your selector should be either simple, or memoizable using reselect to avoid expensive calculations. You didn't show you selector code so we can't tell from here. :)

my reducer is executed but state does not update in react

I am using recat redux for my project and in one component I need to update my state but since I am dealing with asynchronous call I need to do the action call in my componentDidUpdate as follows:
componentDidUpdate() {
this.props.updateHamburgerMenu(this.props.Channel.channelIdArr);
}
and here is my action:
export function updateHamburgerMenu(channelIdArr) {
return dispatch => {
dispatch(
{
type: "UPDATE_HAMBURGER_MENU",
payload: {
"channelIdArr":channelIdArr
}
}
);
};
}
and in my reducer I have :
switch (action.type) {
case "UPDATE_HAMBURGER_MENU":
var channelList=state.allChannelList.slice();
channelList.unshift({
"id": channelIdArr[0],
"channelName": "sssssssss",
"status": "Inactive"
});
alert("reducer called");
state.allChannelList=channelList;
break;}
return state;
Now when I run it I can see that the alert is working but state does not update at all.
Also I tried another way as follow:
state={"channelsArr":state.channelsArr,"AllChannels":state.AllChannels,"channelIdArr":state.channelIdArr,"channelLabelForScrolls":[], "latestAction":action.type, "allChannelList":channelList};
break;
This way, it seems that state keep updating and it goes in infinite loop.
It is really confusing, can anyone help? what am I missing?
Update:
When I separate the allChannelList in another reducer it works. So it seems that updating allChannelList in a specific case of componentdidupdate goes to infinite loop and state keep updating itself. BUt I have no idea why it is happenning
in your reducer case statements, you should be returning a new object which represents the state after the current action - you appear to be trying to assign directly to the allChannelList property on the passed in state object.
i.e.
return {
...state,
allChannelList: channelList
};

Resources