I am new to Redux and i am finding some problems with understanding concept of reducers,
I can see many examples showing it takes current state and return updated state,
My question is how it updates the Store by returning the new state ( i am finding difficult to understand the mechanism ),
can some one please explain me .
The Redux store is nothing but just an object holding all states of the application. The reducer is the only way to update the store.
The reducer is a pure function with takes an old state and returns a new state. In reducer what we need to do is that we just provide the old state which is store currently having and then the new state which we are going to change state. You can refer this for detailed explanation for reduce function.
In simple words, reducer takes existing state object updates some property passed through reducer function and returns new object state.
Following link has better explanation. This is very nice blog how to create your own redux. You will get exactly what happens in the redux store.
https://www.jamasoftware.com/blog/lets-write-redux/
this is image that i found very helpfull when i was learning the same concept.
Dispatch
When you dispatch any function it goes to all reducers and if the type of dispatch matches it will change the state of that reducer.
functionName:()=>(dispatch)({type:'some-thing-to-match',payload})
Reducers
That handle state change.
Store
Combination of all the reducers (root reducer).
const store = combineReducers({
Reducer1:r1,
Reducer2:r2,
Reducer3:r3
})
For example take the dispatch function in TodoList that matches in r1 and changes its state.Then by connect from 'react-redux' we will connect that reducers state to TodoList.
var mapStateToProps = state=>{
return:{
r1:r1
}
}
then react will react to any change in state. If state of r1 is changed then it will update that component.
Your question how it update store by returning state. Your reducer will get store(state) and function as input and change the store according to function and return the state to store.
Then we can connect our component to that store to catch any change in it.
As we can see in image. Dispatch will change the store's state.Then
you can import(connect) that reducer to see the changes in your
component.(here TodoItem is that component)
Actually this is the part that i was missing about reducers,The part i didnt catch was reducers out put has to be assigned to store property
let Action={type:'SET_VISIBILITY_FILTER',text: 'test pay load'}
//Invoking Reducer
let store=todoApp({},Action)
//Reducer
function todoApp(state = initialState, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return Object.assign({}, state, {
message: action.text
})
default:
return state
}
}
Related
So in short I am trying to pass a reducer to a context file and have it so that if the state of reducer is updated then I can view that new state in the context file.
Here is my reducer:
function StillUploadReducer(state = true, action){
switch (action.type){
case 'stillUpload':
return action.payloadData
default:
return state
}
}
Heres how im passing it to a firebase context file:
const addData = await firebase.addExercise(
Data,
props.StillUploadReducer
);
In the context file i do a console.log of the value and i get the original value passed in. The main issue with this is I want the ability for the user to change the state of the reducer while I await on a video being uploaded so that if the state has changed then it is reflected in the context file.
The user changes the reducer using the action:
const StillUpload = (dataToPass) => {
return{
type: 'stillUpload',
payloadData: dataToPass,
}
}
I have console logged the value of the reducer outside the context file and i can see it is being updated I just dont know how to see that change reflected in the context file. Does anyone have any ideas? Thx ahead of time.
A reducer is just a pure function. It has no state, so passing the reducer as an argument to another function cannot achieve what you want.
I'm not sure what you mean by "context file" or what firebase.addExercise does, but you should most likely instead use the useSelector hook which is exactly designed for a component to rerender every time the corresponding part of the state changes.
You should also not use mapStateToProps anymore which is a legacy API for the legacy class components.
I am refactoring React app that was built using class syntax and connect to React hooks.
App is meant to be for debugging and one of the bugs that has to be fixed is that in the reducer file we are not returning the new state object so the component doesn't rerender.
export default function comments(state = InitialState, action) {
switch (action.type) {
case "CHANGE_SORT": {
state.sort = action.sort;
return state;
}
default:
return state;
}
}
This doesn't cause the app to rerender so the state doesn't update and that is fine. However, once I refactored the app to use React hooks and useSelector the component rerenders even with this code. Is there a way I could make it not rerender unless the new state object is returned?
This is how the store is setup:
const store = createStore(
combineReducers({
posts,
sort,
}),
{},
applyMiddleware(thunkMiddleware)
);
The first problem is that your reducer is mutating the existing state, and you can never mutate state in a Redux app.
Both useSelector and connect will cause your component to re-render if you return new references. You'd need to show your useSelector for me to give a more specific answer as to what's going on.
Also, you should be using our official Redux Toolkit package to write your Redux logic, including using the createSlice API to write your reducers. It will drastically simplify your Redux code.
suppose if i dispatch an action using dispatch() , i know that reducer() is called which has an action object and current state as parameters . i want to know what calls that reducer function ? which functions are called before reducer function and after dispatch function ? after reducer function returns the new state ,which functions are called after that ? where does this new state goes ? does usestate() and useselector() also returns something after reducer function returns new state ?
i want to know what calls that reducer function ?
which functions are called before reducer function and after dispatch function
dispatch() indeed 'call' every reducers. It uses an event system and all reducers are listening to this event (this is all behind the scene).
Still, you can write a piece of code that will be inserted between the dispatch call and the reducers catching actions.
It's called a middleware.
A middleware can intercept any action triggered by a dispatch, also it has access to the store.
At the end of your middleware your just use a callback to tell the flow to continue.
You need to pass your action to it so your reducers can finally be called and receive the action.
Here is an example of middleware that log any actions that are sent by any dispatch()
const logger = store => next => action => {
console.log('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
return result
}
To make it work you need to pass your middleware to your redux configuration, so they can be called.
after reducer function returns the new state ,which functions are
called after that, where does this new state goes ?
If you look at your redux configuration you'll see somewhere that you combine all of your reducers ( often called root reducer).
const combinedReducers = combineReducers({ reducerA, reducerB })
const store = createStore(combinedReducers )
Redux use this combination of reducers to fill the store, so anytime a reducer return it's result, it can be save in 'the store'
does usestate() and useselector() also returns something after reducer function returns new state ?
useSelector() is a hooks that has the ability to read in the store. (Store that contains the fresh result of your reducers and is updated every time there is a modification in the store)
useState() is not related to redux. It's related to React. With useState you can read and write in the local state of a component.
It returns you a piece a your state and a setter for this piece of state.
const {myPieceOfState, setMyPieceOfState} = useState({ something :'a default value for myPieceOfState'})
My questions is a conceptual one and based on the issue outlined in this post: React Redux capture updated store state after updating database. I don't think any code is needed to understand or be able to answer it. But if not it is at the link above.
I think I might have missed a small detail about the react/redux state update process following an action that changes the back-end data that a state variable reflects. My question is: When I dispatch a save action, should I then also be dispatching a request to update any state that depends on that underlying data?
So for example, right now the way I'm thinking about it and implementing my code is as follows:
app starts and ParentComponent loads and dispatches GET_DATA on componentDidMount which initializes state variable data which is reflected on ParentComponent in a table
when a link is clicked on ParentComponent, ParentComponent renders ChildComponent which is a react-modal popup that displays elements of data so it can be updated
there is and Save and Close button on ChildComponent; when you click the button, SAVE_DATA is dispatched and the changes to data that are made on ChildComponent get saved to the database
THIS is where my question arises... at this point should I also be calling GET_DATA to dispatch the process of "refreshing" data in my state? Would this be the right way to handle saving data to a database when using redux so that all components that rely on data get updated?
Note: What I'm currently doing is that after step 3, I am simply triggering a refresh function in ParentComponent so that it rerenders and hence reflects data in state. The epiphany I just had is that there is no way for data in state to reflect the new saved data because GET_DATA has not been dispatched after saving and rerendering the component does not trigger GET_DATA.
Are my assumptions correct? Should I be calling GET_DATA somewhere else in my ParentComponent like ComponentWillReceiveProps? The issue I had here is that maybe I'm doing something wrong, but it triggers an endless loop. Somehow though I feel that is the only place where I can address my need to dispatch GET_DATA after the local ParentComponent state is changed by setting refresh (a ParentComponent state variable) to true.
I think it would benefit you to refactor your actions a bit to take advantage of the action/middleware/reducer pattern.
You would have an action GET_TRANSACTIONS, that would take your year param. Your transactionsMiddleware would respond to the GET_TRANSACTIONS action by making your fetch request and would dispatch GET_TRANSACTIONS_SUCCESS with the respond data on success. You transactions reducer would then process the data into your store.
actions
export const getTransactions = year => {
return {
type: "GET_TRANSACTIONS",
year
};
};
export const getTransactionsSuccess = payload => {
return {
type: "GET_TRANSACTIONS_SUCCESS",
payload
};
};
middleware
function getTransactions(year) {
fetch().then(response => dispatch(actions.getTransactionsSuccess(response.data));
}
reducer
const getTransactionsSuccess = (state, action) => {
return Object.assign({}, state, newStuffFromActionPayload);
}
You would also have an action SAVE_TRANSACTIONS, which would be what your button would dispatch, along with the data to save. Your transactionsMiddleware would respond to the action by dispatching the update request. Your API would return the data from the updated record.
This is where you would have the middleware dispatch a follow-up action. It could be your getTransactions action, but it'd be even better to dispatch an action that your reducer would respond to by merging in the new data to your store.
actions
export const updateTransaction = payload => {
return {
type: "UPDATE_TRANSACTION",
payload
};
};
export const updateTransactionSuccess = payload => {
return {
type: "UPDATE_TRANSACTION_SUCCESS",
payload
};
};
middleware
function updateTransaction(transUpdate) {
fetch().then(response => dispatch(actions.updateTransactionSuccess(response.data))
}
reducer
const updateTransactionSuccess = (state, action) => {
find the record in the state, update it with data from action.payload
return Object.assign({}, state, updatedRecord);
}
If everything is set up correctly, it should trigger an update on your parent when it detects the change in the store. You avoid making two API calls for every save as well.
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. :)