How update array in real time not after refresh page (Redux) - reactjs

I'm doing to do list and want do the functional when you click on button "done" the text will be crossed out.
I done array with deals which have fields 'text' and 'isDone'. isDone by default is false, when on click I get all array with deals and text deal in which you click. Than I map array with deals that I get and compare text from click deal and text in all deals in array.If they the same I change isDone from false to true.
But it update if i refresh the page and I need that it updata on click.
I use redux-persist and all states put into localStorage
button
<button onClick={()=>this.props.done(this.props.deals,value.text)}>Done</button>
Action
export function done(newDeals,dealText){
return(dispatch) =>{
newDeals.map(value=>{
if(value.text === dealText){
value.isDone = !value.isDone
}
})
dispatch(doneDeal(newDeals));
}
}
export function doneDeal(newDeals){
return{
type: DONE,
newDeals
}
}
Reducer
export default function toDoList(state = initialState, action){
switch(action.type){
case DONE:
return {
...state, deals: action.newDeals
}
default:
return state
}
}
I delete code that have no sense for this example, but need more info please ask I will tell
Thank you!

You have to use mapStateToProps to get the recently updated state from Redux state.
All what you need is to wrap your component export with the following:
const mapStateToProps = state => ({
deals: state.deals
});
export default connect(mapStateToProps)(ComponentName);
By this you are getting the needed state data from the initialState you have defined in your reducer and the reference to these data is the "deals" which can be used as normal prop: this.props.deals in case of class component OR as parameter through descructureing ({deals}) in case of functional component.
I don't know the full structure of the component because you haven't added it but this is the correct way to get the state from redux.
To make things more clear for you, you can read more through this link:
https://react-redux.js.org/using-react-redux/connect-mapstate
UPDATE: I figured out your problem after adding your reply.
The problem is with your code here:
<button onClick={()=>this.props.done(this.props.deals,value.text)}>Done</button>
You are getting the deals directly from the Redux global state and when dispatching your action you are passing them directly in the dispatch method. So your component is not able to listen to any change happening to your component. You need to save the deals in your local state:
state = {
deals: this.props.deals
}
and change the onClick to the following:
<button onClick={()=>this.props.done(this.state.deals,value.text)}>Done</button>

Related

How to pass Redux Reducer pointer to React Context?

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.

Redux pass up/refresh state from current page

I have a react app (repo) that I want to use redux to store the state universally, so the root app can access it.
For example: one page has a GET API call that populates the page. That works fine and all, but I'm confused as to how to do a couple things.
How can I use variables in the redux action, to give the action say the ID of the model and have it return the model (API returns json).
How can I then pass that state up so that a higher ordered component (such as the base App.js) can access the state, so that I can use variables from the current page in the navigation.
What/when is the best way/time to update the redux state so that the changes reflect across anywhere using the redux state?
Specifically (in this project): If you are on localhost/spells/X with X being the model ID, how can I pass the state up from that page's container component (in this case LayoutSpellView) up to MaterialUIApp
index.js
|--App.js
|--MaterialUiApp
|--Router
|--LayoutSpellView (pass state up to MaterialUiApp)
With Redux you don't pass the state up or down. You update the global state with your action creators and reducers. Wherever you need to reach the state you connect your components to the state and use it. You have a store and it includes a global state. That global state may contain multiple different states.
You can use payload or any other name, variable with your action creator. In your reducer you can get those with action.payload, action.id, etc.
As I explained in the first paragraph, you update your state whenever you need. After that you connect any component to your state wherever you need.
There is no best time or best way to do that. This is up to your code and app logic.
Of course there are some best practices but we can't talk about them so broad. After you are getting involved with Redux you will see some of them around. For example I said "we don't pass up or down the state with Redux". This is true but sometimes to avoid so many connects around components we use container apps, connect that app to store (you reach state via store actually) and then pass the related state parts to the related components.
I recommend Redux's own documentation as starting point: https://redux.js.org/
To help you see the data flow, here's a sketch of how everything ties together. In my example code below, this is the data flow:
Clicking the "Load Comments" button dispatches a thunk with the parameter userId. (A thunk is an async action.)
The thunk uses the userId to make its async call, and then dispatches an action setComments(comments) with the received comments as its payload.
The Comments reducer catches that action and updates the Redux state with the comments array.
The Container to updates comments in mapStateToProps
The Component receives the updated comments, and displays them in the <ul>
// actions.js
export const SET_COMMENTS = "MyApp/setComments";
export const setComments = comments => ({
type: SET_COMMENTS,
payload: comments
});
// thunks.js
import { setComments } from './actions';
export const getCommentsAsync = id => dispatch => {
return axios
.get(`http://localhost:5000/comments/${id}`)
.then(comments => dispatch(setComments(comments)));
};
// reducer.js
import { SET_COMMENTS } from './actions';
const initialState = {
comments: []
};
export const reducer = (state = initialState, action) => {
switch (action.type) {
case SET_COMMENTS:
const comments = action.payload;
return {
...state,
comments
};
default:
return state;
}
};
// components.js
export default function CommentsList({ comments, loadComments, userId }) {
return (
<div>
<ul>
{comments.map(comment => <li key={comment.id}>{comment.body}</li>)}
</ul>
<button onClick={() => loadComments(userId)}>Load Comments</button>
</div>
);
}
// containers.js
import { connect } from "react-redux";
import { getCommentsAsync } from "./thunks";
import CommentsList from "./components";
mapStateToProps = state => ({
comments: state.comments,
userId: state.user.id
});
mapDispatchToProps = {
loadComments: getCommentsAsync
};
export default connect(mapStateToProps, mapDispatchToProps)(CommentsList);

immutable react reducer state is not updating

I am trying to create a simple website using react-redux and the immutable-assign library (instead of immutable) to handle my state. (documentation for immutable-assign: https://github.com/engineforce/ImmutableAssign)
I've made solutions with both the 'immutable' and 'immutable-assign' libraries, but neither work (code for immutable solution is commented out in the reducer below. No matter which changes I make, the state never changes, and the values are never assigned to menuItems
The setMenu(newMenu) function is currently called with dummydata in the form of a list of arrays in the following format:
menuItems: {
id: "113",
foodItem: "tesatewr",
description: "gfdgsdfsdf",
price: 999
}
The reducer:
import { iassign } from 'immutable-assign'
export function setMenu(newMenu) {return {type: 'SET_MENU_ITEMS', newMenu}}
const initialState = {
date: 'test',
menuId: 'test',
menuItems: []
}
function menuViewReducer(state = initialState, action){
switch(action.type){
case 'SET_MENU_ITEMS':
var itemList = iassign(
state,
function (n) { n.push('testtest'); return n}
)
return state.set(['menuItems'], itemList)
default:
return state
}
}
/* CODE FOR IMMUTABLE
function menuViewReducer(state = fromJS(initialState), action){
switch(action.type){
case 'SET_MENU_ITEMS':
return state.updateIn(['menuItems'], (menuItems) => menuItems.push(fromJS(action.newMenu.menuItems)))
default:
return state
}
} */
export const menuSelector = {
date: state => state.menuViewList.date,
menuId: state => state.menuViewList.menuId,
menuItems: state => state.menuViewList.menuItems
}
export default menuViewReducer
Render function:
render(){
return (
<div>
Test data here: {this.props.menuItems}
<ul className="menuViewList">{ this.mapMenuItemsToListElements() }</ul>
<button
onClick={() => this.mapMenuItemsToListElements()}> get data
</button>
</div>
)
}
It's really hard to figure out what's not working from this code. The best I can do is give you some debugging tips:
First off, are you getting any errors? If yes, that seems like a good place to start.
Otherwise, try to narrow down where the problem is occurring.
Are you sure your reducer is actually getting called?
I would try putting a console.log right after your case 'SET_MENU_ITEMS': so you know when your code is being run.
If it's not:
The problem could be a number of things:
Your reducer isn't connected to your store properly
You're not properly dispatching actions to your store
The actions you're dispatching don't have their type property properly set.
If it is:
The problem could be a number of different things. Some that I can think of:
Your state isn't being updated (properly). Try logging the state at the start of your reducer and your new state right before you return it. Or consider using redux-devtools to inspect your state.
Your view isn't getting updated. Maybe your component isn't connected to your store properly.
I found the error and as Simon pointed out, its hard to find from my submitted code.
I was calling setMenu(newMenu) in a generator function, so I should have called it like this:
yield put(setMenu(newMenu))
instead of
setMenu(newMenu)

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. :)

How to wait for an action to complete?

I'm new to react and redux and have been exploring how it is different from angular.
I'm trying to do a simple login page and am not able to achieve what I want. Once I get the username and password from a login form, I submit it like below :
submitHandler(e){
e.preventDefault();
var user = {
name : this.state.username,
password: this.state.password
}
this.props.authenticateUser(user);
browserHistory.push('/home');
this.setState({
username:'',
password:''
})
}
Here, authenticateUser is a function inside my reducer (shown below) which checks from an already existing user's list and then the state with a property isAuthenticated :
case 'AUTHENTICATE_USER':
var users = state.userslist;
var payloadUser = action.payload;
for (let i = 0; i < users.length; i++) {
let user = users[i]
if (user.name === payloadUser.name) {
return {...state, isAuthenticated: true };
}
};
return {...state, isAuthenticated: false };
Now in Angular, I would just wait for this call to finish and check whether this value is true or false, and navigate to the home page using angular-router.
But how would I achieve the same in react. I am using react-router in my sample application.
I tried to check isAuthenticated in my submitHandler function right below where I call this.props.authenticateUser(user) , but the problem is , this.props.isAuthenticateUser is not updated and returns false, which I have set as the initialState in my reducer.
Please tell me how I can check isAuthenticated after the authenticateUser function has called so that I can proceed with my homepage routing. Do I have to use a promise and wait for the state to be updated by redux and then check it again ?? (I know react is a one way path and it doesn't work that way.)
Also if my authentication fails, how do I show an error panel below my login form. I think using a component for the error panel which takes in isAuthenticated as input and checking if false in order to show (ie. ngShow like in Angular.) But I'm afraid that will not work as well, since my state will not be updated just in time.
I know that the render function will be called with any change in state. But I just can't wrap my head around React clearly.
Edit: For brevity, I have not provided the action creators and the full reducer above rather shown only the switch case operation for authenticate user. My app follows the original redux architecture with actions, reducers, containers, components and such.
EDIT: Github link here
I will try to explain briefly how data flow works in a Redux application:
The reducer should not be called directly from the component. Your call to authenticate user should be inside an action and that is what should be dispatched from the component.
When you define the action, you can give a property to define the type of action it is. For example, in this case, it could be type: AUTHENTICATE_USER
After the completion of the action, the redux store calls the reducer where with the current state and the action. Here, the redux state can be updated based on action.type (the second code snippet above)
This is the most important part: your component needs to have a mapStateToProps method which passes values from your Redux state as props into your component. So as the state changes, the props get updated and the component re-renders. To use mapStateToProps, you need to implement connect from the react-redux library. (https://github.com/reactjs/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options)
To achieve what you want, this is what I suggest:
In your component's componentWillReceiveProps method, checking for props.isAuthenticated and using context.router.push() to navigate if the user is authenticated.
Also having a check based on props.isAuthenticated in the component's render method to show the error message.
A few links which could be useful:
https://redux.js.org/basics/data-flow
https://redux.js.org/basics/usage-with-react
I prefer code like this:
const mapStateToProps = (state) => {
return {
isAuthenticated: state.isAuthenticated
}
}
class Dashboard extends PureComponent {
render() {
const { isAuthenticated } = this.props
return (
<div>
{ isAuthenticated ? (
<div>
<p>Hello</p>
</div>
) : (
Login()
)}
</div>
)
}
}
Source: https://stackoverflow.com/a/56899814/3850405
Addition to the answer from #Nupur. componentWillReceiveProps() is considered unsafe and the method name now is: UNSAFE_componentWillReceiveProps()
https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops
Use componentDidUpdate() instead if you would like this approach, this method is not called for the initial render.
componentDidUpdate(prevProps) {
if (this.props.isAuthenticated ) {
//perform code here
}
}
https://reactjs.org/docs/react-component.html#componentdidupdate

Resources