How to wait for an action to complete? - reactjs

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

Related

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

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>

React/Redux - How to call an action after component has rendered, based on a check - Getting error

I have, what should be a simple issue in which, when a user navigates to a specific route, the component fires an initial function call to grab a user by ID via a redux-observable in an epic. However, if the user navigates away from the page and then comes back, I need to be able to reload the page, based on a route parameter.
I have a component that utilizes an HOC to run the render() method, but it looks like a dumb component:
const ProfilePage = props => {
const { actions, user, loading } = props;
// Note: This if statement results in an error
if (user && user.id !== props.params.id) {
actions.initAction(props.params.id);
}
return (<div>Test</div>);
};
ProfilePage.propTypes = {
actions: PropTypes.object,
user: PropTypes.object,
loading: PropTypes.bool,
};
export default connect(
state => ({
user: selectUser(state),
loading: selectLoading(state),
}),
dispatch => ({ actions: bindActionCreators(Actions, dispatch) })
)(
PureRender(
onMount(props => props.actions.initAction(props.params.id))(ProfilePage)
)
);
This results in an error:
react-dom.development.js?61bb:506 Warning: Cannot update during an existing state transition (such as withinrender). Render methods should be a pure function of props and state.
^ This happens because of my if statement that checks the user id against the params id.
Does this component need to be converted into a class in order to utilize other life cycle methods that could prevent this error from happening and run my functionality accordingly?
Function component should be pure, you can think of them as the "render" method of a class component.
You can either use a class component and do side effects in componentDidMount / componentDidUpdate, or use hooks with useEffect.
hooks useEffect / class cycle methods
I am guessing the issue is with ProfilePage. Added error Cannot update during an existing state transition (such as withinrender). thrown mostly when a setState which is a async method, another setState gets called. For your case it's a functional component which you are using it in render method. So basically each time your component rerenders ProfilePage function gets called which calls the action. So you might wanna change ProfilePage this to a react component and use life cycle methods such as componentDidMount to fix your issue.
Or if you are using react 16 or above use hooks with useEffect.

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

converting react classes to functions with redux

I'm still new to react/redux, after getting something like this to function
User.js
class User extends React.Component {
componentWillMount() {
this.props.fetchUser(.....);
}
render() {
return (
<Profile />
)
}
export default connect(null, {fetchUser})(User);
Profile.js
class Profile extends React.Component {
render() {
const { user } = this.props
return (
<h1>{user.profile.name}</h1>
)
}
const mapStateToProps = state => ({
user: state.store.user
});
export default connect(mapStateToProps, {})(Profile)
actions.js
export const fetchUser = (.....) => dispatch => {
fetch()
.....
}
reducers.js
case FETCH_USER:
return {
...state,
user: action.payload.user
};
As I understand it, the User component calls an action (fetchUser) from connect on componentWillMount(). That action calls an api, gets the data and the reducer adds that to the store within the state. The Profile component can then use connect to map the data from fetchUser in the store and display that data.
After reading some tutorials including https://github.com/reactjs/redux/blob/master/docs/basics/UsageWithReact.md
It looks like things can be simplified a bit without using classes.
If I were to change the User and Profile components to a more functional way, how would I do it?
eg.
const User = () => {
return (
<Profile />
)
}
how do I dispatch the fetchUser action and how do I simulate it to be called with the flow of componentWillMount()?
or am I just over complicating things?
There is also a way to support lifecycle methods in functional components.
https://www.npmjs.com/package/react-pure-lifecycle
import React from 'react';
import lifecycle from 'react-pure-lifecycle';
// create your lifecycle methods
const componentDidMount = (props) => {
console.log('I mounted! Here are my props: ', props);
};
// make them properties on a standard object
const methods = {
componentDidMount
};
const FunctionalComponent = ({children}) => {
return (
<div>
{children}
</div>
);
};
// decorate the component
export default lifecycle(methods)(FunctionalComponent);
I think you should keep using statefull components with redux...
https://medium.com/#antonkorzunov/2-things-about-purecomponent-you-probable-should-know-b04844a90d4
Redux connect — is a PureComponent.
Yes — a very important thing, a HoC for a molecule is a pure one. And works even inside other pure components. And gets store from a current context.
Same is working, for example, for styled-component — you can wrap it with PureComponent, but it will still react to Theme changes.
Solution is simple — bypass logic, use old school events bus, subcribe, wait and emit events.
Styled-componets:
componentWillMount() {
// subscribe to the event emitter. This
// is necessary due to pure components blocking
// context updates, this circumvents
// that by updating when an event is emitted.
const subscribe = this.context[CHANNEL];
this.unsubscribe = subscribe(nextTheme => { <----- MAGIC
React-redux:
trySubscribe() {
if (shouldSubscribe && !this.unsubscribe) {
this.unsubscribe =
this.store.subscribe(this.handleChange); <----- MAGIC
}
}
componentDidMount() {
this.trySubscribe();
}
Thus, even if parent Pure Component will block any update enables you to catch a change, store update, context variable change, or everything else.
So — something inside pure components is very soiled and absolutely impure. It is driven by side effects!
But this bypass straight logic flow, and works just differently from the rest of application.
So — just be careful. And don’t forget about magic.
Aaaand….
And this is a reason, why any redux store update will cause redraw in each connected component, and why you should use reselect just next to connect HoC —
to stop unnecessary change propagation.
But you should read this from another point of view:
redux-connect is a source of a change propagation.
redux connect is the end of a change propagation. It is still PureComponent.
And this leads to quite handy thing — you can control change propagation with redux-connect only. Just create a boundaries for a change. Lets talk about this in another article.
Conclusion
Pure components keep your application fast. Sometimes — more predictable, but often — less predictable, as long they change the way application works.
Stateless components are not pure, and may run slower than PureComponents by any kind.
But… if you very wish to create a fast application with good user experience — you have to use Pure Component.
No choice. But, now — you know hidden truth, and knew some magic…
React recommends that ajax request be made in componentDidMount(), rather than in componentWillMount(). For more info on this, read this post.
Since you want to make ajax requests in componentDidMount(), you need a class. There are two ways of writing component definitions: functional component and the class component. Functional components are more concise, but you don't get component lifecycle methods like componentDidMount(). Think of it as just a render function that takes props as inputs and outputs DOMs (in JSX). To override those lifecycle methods, you need to define them as a class.
If you want to use Redux, and want to make ajax requests in a Redux action, you should import the action creator function (fetchUser(..) in your case) that makes the ajax request, and dispatch(fetchUser(..)) in componentDidMount(). connect(..)ed components get dispatch(..) function passed to it by Redux store.
If you want to see how it's done in other redux apps, see the official example apps in the redux.js repo, paying attention to actions and containers: https://github.com/reactjs/redux/tree/master/examples
In Your case you can continue with statefull components no wrong in that
,If you need to go with functional way
There is a work arround
https://github.com/mobxjs/mobx/issues/162
Suggestion
Calling the api in componentDidMount will make sense than
componentWillMount , Because you can show the user something is
fetching.
I think,User component is designed nicely.It will act as a container for Profile to provide the Data.
Instead of making Profile component class oriented,it should be Stateless.
Lets User component pass the required data for Profile component.
You don't need to connect Profile component using redux-connect.Just render it as a Child component of User.
Profile
const Profile = (props) => {
const {user, likeProfile} = props;
//likeProfile()//call like this using dom event or programmatically.
return (
<h1>{user.profile.name}</h1>
)
}
You need to make some changes in User component.
Get the state for Profile component via mapStateToProps.
class User extends React.Component {
componentWillMount() {
this.props.fetchUser(.....);
}
render() {
const {user, likeProfile} = this.props;
return (
<Profile user= {user} likeProfile={likeProfile} /> //passed the user data to Profile component vua User
)
}
Map the user state for Profile in User connect.
const mapStateToProps = (state)=>{
return{
user : state.somereducerkey.user //this will be accessible in Profile via props { user}
}
}
export default connect(mapStateToProps, {fetchUser, likeProfile})(User);

hello world reducer for react/redux

There are a few other hello world app questions with regard to react, but mine is specific to the reducer. Im not exactly sure what I should put in the reducer for my specific action.
Note*: I thought maybe i need to add a message: "" key value pair to my initial state and then declare a
var newState = Object.assign({}, state, {
message:"hello world"
});
into my if statement in the reducer, then dispatch it in the component, but that seems unneccesary since it should always print hello world, so hard coding seems more efficient. Hopefully there isn't too much clutter in this question as well.
Here is my component:
var HelloWorld = React.createClass({
helloWorld: function() {
this.props.dispatch(actions.printHello());
},
render: function() {
return (
<div className="HelloWorld">
<h1>Hello World!</h1>
</div>
);
}
});
var Container = connect()(HelloWorld);
module.exports = Container;
my action:
var $ = require("jquery")
var PRINT_HELLO = 'PRINT_HELLO';
var printHello = function() {
return {
type: GUESS_NUM
};
};
exports.PRINT_HELLO = PRINT_HELLO;
exports.printHello = printHello;
and reducer:
var actions = require('./actions');
var initialRepositoryState = {
type: null
};
var capstoneApp = function (state,action) {
state = state || initialRepositoryState;
if (action.type === actions.PRINT_HELLO) {
var newState = Object.assign({}, state, {
message:"hello world"
});
return newState;
}
};
I don't think you will need my index.js but I will provide if necessary.
Thanks in advance for input!
The reducer holds your state. So the way of thinking about it is "what are the parts of my program that can change?" And then from there, the next question is "What is the minimal amount of state needed to hold on to my program?"
From looking at your example, I get the impression that you are trying to build an application that sometimes displays "hello world" in the UI. Let me make a few more interactions to help describe all the pieces.
Initially, I'll help you create a program that has an empty label and a button. When you click on the button, it will display "hello world".
Okay, so to answer the first question: What can change? The app can either display "hello world" or nothing. We could store that in a couple of different ways. If the string is hard-coded, like you've alluded to above, then really you have a bool of show the message, or not.
And to the second question: one truthy value is pretty much the definition of a minimal state. So let's make a reducer:
var initialState = {
showMessage: false,
};
var reducer = function(state, action) {
var newState = Object.assign({}, state);
if (action.type === 'BUTTON_PRESS') {
newState.showMessage = !newState.showMessage;
}
return newState;
}
Okay, so now we've created a reducer that, when it gets the BUTTON_PRESS action, it flips the bit of its state.
Now, let's talk about how to hook that reducer up to redux. Right now, the reducer is a plain javascript function. We just need to pass that store into redux with the initial state. [createStore][1]
P.S. I normally write ES2015 code so there may be small typos in the commonJS import syntax
var redux = require('redux');
var store = redux.createStore(reducer, initialState);
The next part is to look at redux-react.
Redux-react is glue that works both ways in react. It connects data from the redux store to react props, and it connects react callbacks (such as a click) to redux actions.
So conceptually, it looks like this. You have a react component that has a button. When you click the button, we want to generate a 'BUTTON_PRESS' action. After this, your react component no longer cares what happens to BUTTON_PRESS. Its job is done. BUTTON_PRESS could do one of infinite things. Now, assume that redux-react does its job and gets the action passed to the reducer. The reducer computes its new logic and returns a new state. This new state has the value of showMessage. Then, redux-react does the other half of connecting and makes showMessage a prop for the component. Just to be clear, there is no explicit reason why the same react component has to respond to the state changed by your action. They could be different components.
To put it into bullet points, here is how the codeflow should work:
We create an initial store with showMessage = false
When creating the React component, we use connect to bind the showMessage to a prop, and to handle onClick of the button in the component to generate a 'BUTTON_PRESS' action.
Since showMessage is false, there is originally only a button present.
The user presses the button. React calls into the onClick handler
We use redux-react to dispatch a BUTTON_PRESS event
When redux gets an action, it calls the reducer with the current state and the action. The reducer is responsible for generating a new state in response to this action
The reducer sets showMessage to true
redux-react listens to store changes and when it changes it modifies the prop for the react component
The prop changes so react calls render()
Inside your render method you see that this.props.showMessage is true, so you display the message.
And here is how such a react component could be implemented. There are enough differences between React components in ES5 vs ES2015 that I'm just going to give you the ES2015 version and apologize again.
class HelloWorld extends Component {
render() {
const message = this.props.showMessage ? "Hello world!" : "";
return (
<div id="label">{message}</div>
<div id="button" onClick={this.props.handleOnClick}>Toggle the message </div>
);
}
}
HelloWorld.propTypes = {
showMessage: PropTypes.bool.isRequired,
handleOnClick: PropTypes.func.isRequired,
};
const mapStateToProps = state => ({
showMessage: state.showMessage,
});
const mapDispatchToProps = dispatch => ({
handleOnClick: dispatch({ type: 'BUTTON_PRESS', payload: '' })
});
export default connect(mapStateToProps, mapDispatchToProps)(HelloWorld);
You can keep reading the docs but I hope that helps explain what all the parts are. Let me know if anything isn't clear.
[1]: http://redux.js.org/docs/api/createStore.html

Resources