attach a loading component on submit using react - reactjs

I have a work around that I have been trying to attach a loader or progress bar component after clicking the submit form. I have the loading declared in redux initial state but don't know how to proceed from here.
Yes, I know how to attach the loading component while fetching data from an API using GET request but don't know how to utilize with POST request. So, the form should load a component which clicking to submit and then it will send the data to the store.
Following is the sandbox link:
https://codesandbox.io/s/brave-lewin-bp3w6?file=/src/Form.jsx
Any help would be highly appreciated.

Create an additional action startLoading that you can dispatch before the API call:
export const START_LOADING = "START_LOADING";
export const startLoading = () => {
return {
type: START_LOADING
};
};
export default function(state = initialState, action) {
switch (action.type) {
case START_LOADING:
return {
...state,
loading: true
};
case ADD_USERS:
return {
...state,
users: [...state.users, action.payload],
loading: false
};
default:
return state;
}
}
Then in your onSubmit you can do:
props.startLoading();
props.addUsers(values);

Related

How to write a Redux ReactJS modal component correctly?

On one page I use 2 or more modals with different content. Now for each modal window, I create a reducer that stores isOpen: true / false. Is this a normal solution? It confuses me that I have to create a reducer for a new modal window in applications.
I would like to of course use only 1 reducer so that I can reuse the modal window component. And I ran into a problem when, when the modal window was opened, the rest were opened along with it.
Is hard to help with no example code. But based on what you described it looks that you'll need two states. isOpenModal1 and isOpenModal2. You can combine them in a single reducer like this:
import { createStore } from 'redux';
const rootReducer = (state,action)=>{
switch(action.type){
case 'OPEN_MODAL_1':
return {...state, isOpenModal1:true}
case 'CLOSE_MODAL_1':
return {...state, isOpenModal1:false}
case 'OPEN_MODAL_2':
return {...state, isOpenModal2:true}
case 'OPEN_MODAL_2':
return {...state, isOpenModal2:false}
default:
return state
}
}
const initState = {
isOpenModal1: false,
isOpenModal2: false
}
const store = createStore(rootReducer,initState);
export default store;
And when you need to close/open the modal you can use use useDispatch:
const Open1 = () => { dispatch({ type: "OPEN_MODAL_1" }) };

Reducer: getting information about value changes at the store

I don't understand how to get an updated value from the reducer store. For example, I have a React component. In this component after some actions, for example, after a few clicks on a button, I call the action from my reducer actions script like this.props.PostSomethingToServer(). Then the action sends some data to the node express server. The server makes some changes with data and then sends a response to the client store reducer. How can I get the updated data from the reducer store? I need to call another function in this React component with updated values.
By the way, I use mapStateToProps and export default connect() in the React component. As I know, mapStateToProps and export default connect() help to get data from the store before render(). But I still don't understand how to get updated data from the store after some actions.
A couple of code:
React component:
ChangeArrayData(){
let something = [];
// filling this array and modifying values
//then I call the action
this.props.postSomethingToServer(something);//something will be changed by server logic and will be returned with updated data by a response.
//then I wanna export the data to .excel, for example
this.ExportToExcel(); // here I have to get updated data from the reducer, but I don't have any information about changes in the reducer.
}
Reducer action:
export const postSomethingToServer= rankedElements => dispatch => {
axios
.post("/api/postData", elements)
.then(response => {
dispatch({
type: POST_SOMETHING_SUCCESSFUL,
status : "success",
payload: response.data
});
//... etc.
Reducer:
const initialState = {
something: {},
status: "",
error : ""
};
export default function(state = initialState, action) {
switch (action.type) {
case POST_SOMETHING:
return {
...state,
status: action.status,
}
case POST_SOMETHING_SUCCESSFUL:
return {
...state,
status: action.status,
something: action.payload
}
case GET_ERRORS:
return {
...state,
status: action.status,
error: action.error
}
default:
return state;
}
}
You Should assign reducer state values to some local state like following:
`const mapStateToProps = state => ({
contacts: state.data
});
export default connect(mapStateToProps, { actions })
(withStyles(contactStyle)(Contact));`
Here 'contacts' is a local state name we are using in the class and 'data' is a state name that we return from reducer after updating a state.
You can access the updated data using componentWillReceiveProps method like,
`componentWillReceiveProps(nextProps) {
if(nextProps.contacts !== undefined) {
//Handle updated states here
}
}`
After you dispatch the action, your reducer state something should have the data you expect. Given that you have mapped the data in your mapStateToProps function you can access it via props.
ChangeArrayData() {
let something = [];
this.props.postSomethingToServer(something);
this.ExportToExcel();
console.log(this.props.somethingReducerState);
}
const mapStateToProps = (state) => ({
somethingReducerState: state.yourReducerName.something,
});
I have the solution: I'm using componentWillReceiveProps(nextProps) and can receive a result from the reducer.
Thanks all for the answers.

Redux MapDispatchToProps not functioning

So I'm new to Redux and I'm trying to get this base model working so I can quickly work on a small personal project, I set everything up and have no errors but I'm trying to test and my function doesn't work so I was hoping someone could point out what I've missed.
I've followed multiple different tutorials and each has a different approach so that has me lost a bit so I apologize for that.
My store.js looks like so
import rootReducer from "./reducers";
import thunk from "redux-thunk";
const store = createStore(rootReducer, applyMiddleware(thunk));
export default store;
I've used a combineReducers in my index.js in reducers folder and the auth: points to the authReducer.js file, which is this
const INIT_STATE = {
email: "",
password: "",
isLoggedIn: "false"
};
export default (state = INIT_STATE, action) => {
switch (action.type) {
case IS_LOGGED_IN_CHANGE:
console.log(action);
return {
isLoggedIn: action.value
};
default:
return state;
}
};
Now What I'm aiming for is to have a button that changes that "IsLoggedIn" initial state to a true string instead of a false, I've went into my actions folder and made an authActions.js which looks like so
import { IS_LOGGED_IN_CHANGE } from "../actions/types";
import store from "../store";
export const isLoggedInChange = value => {
return dispatch => {
dispatch({
type: IS_LOGGED_IN_CHANGE,
value
});
};
};
And Finally I want to show you my component page which is showing all this, It's looking like so
import { connect } from "react-redux";
import styles from "./Landing.module.css";
import { isLoggedInChange } from "../../actions/authActions";
class Landing extends Component {
makeTrue = () => {
isLoggedInChange("true");
};
constructor(props) {
super(props);
this.state = {
email: "",
password: ""
};
}
render() {
return (
<div className={styles.background}>
<button onClick={this.makeTrue}>MAKE TRUE</button>
{this.props.isLoggedIn}
</div>
);
}
}
const mapStateToProps = state => ({
isLoggedIn: state.auth.isLoggedIn
});
const mapDispatchToProps = dispatch => ({
isLoggedInChange: value => dispatch(isLoggedInChange(value))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(Landing);
Can you tell if I dropped anything making this? why is the button not changing the store state? TIA
Two problems here. You're calling your action creator directly not props.isLoggedInChange
makeTrue = () => {
this.props.isLoggedInChange("true");
};
And you need to spread the old state inside your action
case IS_LOGGED_IN_CHANGE:
console.log(action);
return {
...state,
isLoggedIn: action.value
};
Isn't the point of my mapDispatchToProps to be able to use the function right away as I was doing
Yes, the problem is mapDispatchToProps inject a function (or multiple functions) wrapped in dispatch into your props.
import { actionCreator } from './actions
const mapDispatchToProps = dispatch =>({
actionCreator : () => dispatch(actionCreator)
})
Now you have two actionCreator, one globally available in the scope (which is your action creator) and props.actionCreator which is the original action creator wrapped in dispatch. So when you call actionCreator() from inside your component it won't throw any errors (cause there is a function named actionCreator in the scope, but you will be calling the wrong function, the right one is located at props.actionCreator.
Why do I need to spread the state?
A reducer is a pure function which receives a state and action and returns the new state. When you just return
return {
isLoggedIn : true
}
You're actually overwriting the original state (which contains other properties), so first you need to spread the original state to maintain it's structural integrity and them overwrite the properties you want
return{
...state,
isLoggedIn : !state.isLoggedIn
}
Redux state is immutable so you need to return a brand new instance of state, change your reducer state to the below.
export default (state = INIT_STATE, action) => {
switch (action.type) {
case IS_LOGGED_IN_CHANGE:
console.log(action);
return Object.assign({}, state, {
isLoggedIn: action.value
});
default:
return state;
}
};
The key difference there being the
return Object.assign({}, state, {
isLoggedIn: action.value
});
Object.assign in the way I'm using it here combines the state object into a brand new object. Check out immutability within redux reducers and I'd recommend adding redux-immutable-state-invariant as a dev package, it can detect when you're directly modifying state and help point out errors like this
Return the state with the new value for isLoggedIn. Use the reducer like this:
case IS_LOGGED_IN_CHANGE:
console.log(action);
return {
...state,
isLoggedIn: action.value
};

How to get the value as props in a different component

In my react application, I have three parallel components. In my first component, I am doing an API call and based on the response I am routing the flow to Validated or Non-Validated Component.
Once the user is routed to validated component, there is a button on click of which user should be redirected to another component which should display the data in API response (first component) as key value pair. I am using Redux for state management.
The issue I am facing is the data is dispatched as an empty object from the store. I am not sure where I am going wrong but when I am debugging the app, I see the the action is not getting dispatched to the store and it's always returning me the initial state.
action.js-
export const setPoiData = (poiData) => dispatch => {
console.log('inside actions');
dispatch({
type: SET_POI_DATA,
payload: poiData
})
}
Reducer.js-
const initialState = {
poiData: {},
}
const reducerFunc = (state = initialState, action) => {
switch (action.type) {
case SET_POI_DATA:
console.log('inside poi reducers');
return {...state,poiData: action.payload}
default: return {...state}
}
}
Component 1-
//API call
Detail Component-
To get the data from store I am doing something like below-
componentDidMount() {
console.log(this.props.poiData)
}
function mapStateToProps(state) {
return {
poiData: state.poiData,
}
}
const mapDispatchToProps = dispatch => ({
setPoiData(data) {
dispatch(setPoiData(data));
}
})
I am not sure where I am going wrong. Can someone suggest me how to proceed ahead on this?
inside componentDidMount() you must call action like this this.props.setPoiData(<your data here>);

Generate reducers dynamically (universal reducer for api calls)

In redux how can i make reducers dynamically based on api call passed as string to an action-creator to reduce the boilerplate (so for each api call there was a dedicated key inside the store)?
And should i even try to do that?
Example jsfiddle
The problem is here:
export function universalFetchReducer(state = initialState, action) {
switch (action.type) {
case 'FETCHING_DATA' + action.metadata:
return {
...state,
isFetching: true
};
case 'FETCHING_DATA_SUCCESS' + action.metadata:
return {
...state,
isFetching: false,
data: action.data,
dataFetched: true
};
case 'FETCHING_DATA_FAILURE' + action.metadata:
return {
...state,
isFetching: false,
error: true
};
default:
return state;
}
}
For now i can create actions and their names based on url passed to an action-creator, but cannot make a dedicated reducer.
Solved this by using redux-injector, followed its api to create an action creators and a simple async action creator (axios used):
export function getData(api) {
return {
type: `FETCHING_DATA_${api}`,
meta: api
}
}
export function universalFetchData(api) {
injectReducer(`universalFetch${api}`, universalFetchReducer);
return dispatch => {
dispatch(getData(api)) //Some initial action. Pass api to name actions
axios
.get(api)
.then(response => {
dispatch(getDataSuccess(response.data, api)) //Some success action
})
.catch(error => getDataFailure(error.response.status, api)) } } //Some failure action
Then just fired an universalFetchData('path_to_api') from component and got FETCHING_DATA_path_to_api action in redux-devtools.
Got data from store
state.universalFetchReducer_path_to_api
and passed this state to render with e.g. ramda's pathOr to set unkown initial state.
Lesson learned: you will be able to make many simple lazy loading api calls fast, but do this only if you know what data you're getting. For more dangerous logic use regular reducers upfront. This solution nowhere near acceptable but it gets job done.

Resources