How to set the data in Redux when I am fetching something? - reactjs

Good evening,
I am making weather app with react and redux. I have my reducer with a object of my data info with empty string etc:
const initState = {
city: '',
temp: '',
wind: ''
}
And for example in React I am gonna fetch all the data and what should i do now?
I should have dispatch action (FILL DATA - for example) and then i should make a [useState] inside my component for temporary place for my data. Then fill the data in my temporary place and then useDispatch to update redux store?

Actually, there are a few ways to do this, such as:
Use a redux middleware like redux-saga or redux-thunk, which I recommend.
In the request action, make the asynchronous call and then dispatch the success action to update the state data.
Call the API from the component and call the state redux store update action to update the global state. NOT RECOMMENDED.
Apparently, you're trying to use the third way, but it's not recommended because it beats the purpose of abstract redux and making the data scattered all around. A bad practice.
A middleware example would be very long, so I'll try to explain the second way briefly.
In your request action, do something like this:
...
axios.get(url).then(res => {
dispatch({ type: 'REQUEST_SUCCESS', data: res.data });
});
In your reducer:
...
switch (action.type) {
case: 'REQUEST_SUCCESS';
return { ...state, ...action.data };
}

There is a library called redux-thunk that will help you with this. It allows actions to be asynchronous, so you can actually dispatch the action, fetch all the data INSIDE the action and then dispatch an action to fill your state. After configuring it, your action would look something like this:
const getWeatherData = () => { // Declare it like any other action
return async (dispatch) => { //Then return a promise
const response = await getWeatherAPI(); // fetch from the backend
return dispatch({ // And then dispatch another action to populate your reducer
type: "FILL_DATA",
payload: response
})
}
}
This action would be dispatched from your code just like any other action, but considering it returns a promise you can actually await for it to finish and handle that any way you want.

Related

useEffect hook of react vs redux thunk middleware in redux

use effect hook is used to perform the side effects like network requests in react.
redux-thunk middleware is also used to perform the side effects like network requests in react.
I'm pretty confused, is there any difference in their real application, or is it just a matter of choice.
The purpose of thunk is not to perform side effects by definition.
In Redux world, the actions must be plain objects with a required key type. An example:
const increaseAction = { type: "INCREASE" };
If you want to create a function that returns an action, this function should also return an action object but nothing else. Otherwise you cannot dispatch the action creator function itself.
// Create an action creator
const increase = () => {
return { type: "INCREASE" };
}
// Now you can dispatch the result of this function
dispatch(increase());
However, when dealing with asynchronous network requests, you probably want to dispatch multiple actions that updates your state accordingly based on the current state of your network request.
// When starting network request
dispatch({ type: "FETCH_START" })
// When network request is successful
dispatch({ type: "FETCH_SUCCESS" })
// When network request fails
dispatch({ type: "FETCH_ERROR" })
That's why action creator functions that deals with network requests or asynchronous operations return another function that takes dispatch as its parameter. This return function is handled by thunk middleware. Now we can use the dispatch function from the parameter to dispatch our actions.
const fetchData = () => async (dispatch) => {
dispatch({ type: "FETCH_START" });
try {
const data = await fetch("http://somedata.com/something").then(res => res.json());
dispatch({ type: "FETCH_SUCCESS", payload: data });
} catch {
dispatch({ type: "FETCH_ERROR" });
}
}
If you realized, we did not return anything inside fetchData. Instead, we used the dispatch parameter from the function that is returned by fetchData. When you dispatch(fetchData()), thunk transforms your action creator functions into plain objects; wait for the network requests to be resolved or rejected, then dispatch the appropriate action based on the result of your network request.
Now where does useEffect fall into this equation?
useEffect is the React hook that mimics the React lifecycle methods from class components. If you want to make a network request, or any asynchronous operation, you can do it inside useEffect. Following the Redux example above, you would call dispatch(fetchData()) inside useEffect.
Redux thunk is if you are using redux and are doing something asynchronously. E.g such as writing to a database.
if you are just using functional components in React and you want to update the ui then you would use useEffect to check for the change. If you are using class based components then there is a built in method componentDidMount. Built in as in you don't have to import it in along with React. Which you need to do for useEffect.
Here is the page for hooks, that talks about how it is used.
https://reactjs.org/docs/hooks-effect.html
Here is the page for thunks
https://redux.js.org/usage/writing-logic-thunks

Can a redux action both dispatch and return something in React

I have set up my redux properly: store, actions, reducers, I've mapped state to props etc and I am using redux thunk.
This is what my current actions look like:
I normally use my actions like this inside my components and it works just fine:
this.props.addCars(newCar)
Sometimes I want to add a new car and in the next line do something with the new data using this.props.cars
This.props.cars does not show the car I just recently added though and using a Promise, or await like this does not work
await this.props.addCars(newCar)
So I was wondering if there is a way to make my actions dispatch and return the data response.
You need to wait for the response from axios and then dispatch the results plus return. Something like this.
export async function addCars(car){
var result = await axios.post('http://localhost:8000/api/cars', car)
.then((resp)=>
{
return resp.data
}
dispatch({type: ADD_CAR, payload: result});
return result;
}

ReactJS Redux Api Fetch

I just need a little help with regards to fetching of data through an api
currently I have this code in my container
fetchAll: () => {
Promise.resolve(token.getToken()).then( (response) => {
let obj = {
token_type: response.data.token_type,
access_token: response.data.access_token
}
apiFetch("http://dev.apiserver.com/api/all",{
"method": "GET",
"headers": {
"Authorization": `${obj.token_type} ${obj.access_token}`,
"userId": 1
}
})
.then( (res) => {
return res.json();
}).then( (json) => {
dispatch({
type: 'FETCH_ALL',
payload: json
})
}).catch(function(err) {
console.log(err)
})
})
}
I'm calling the above function from componentWillMount(), I can successfully log the result in my console
here is my reducer code
const allReducer = (state: Immut = initialState, action: { type: string, payload: any }) => {
switch (action.type) {
case FETCH_ALL:
let newState = state
let payload = action.payload
newState = Immutable.fromJS(payload)
console.log(newState)
return newState
}
}
the problem is that it doesn't update the state or re-render the components
Since your reducer is getting called, the issue is likely not in the AJAX call or the update part of the Redux cycle, but in how your component connects to the state. Changes in the state should reactively trigger a re-render of the component, but for this to happen, the props of the component need to change. First, a few steps on how you might debug this issue, before some suggestions of possible causes, and suggested best practices.
Redux Dev Tools
Install Redux Dev Tools on Chrome. DevTools allow you to view a history of actions, and the changes each action induced to the Redux state.
First, use Redux DevTools to ensure that the shape of your Redux state is what you expect, even before triggering any action / AJAX call.
Then, select your AJAX fetch success (FETCH_ALL) action in Redux DevTools and, looking at the diff, see that the state changes as you expect.
Manual debugging
Try putting a debug value in the initialState of your reducer, and ensure it renders on the component.
See that the props of the component are updated when state changes. Put a console.log for this.props and nextProps in the life-cycle method componentWillReceiveProps(nextProps).
Issues to look for
Your component may be looking for the data in a wrong place of the state. The structure of your store follows the composition of your reducers. For example, the following puts the output of myDataReducer under app.db:
const rootReducer = combineReducers({
app: combineReducers({ db: myDataReducer }),
});
You seem to be using Immutable.js. Vanilla Redux treats the state as a POJO. You should either A) use a JavaScript object as the root of your state, or B) use a special combineReducers from redux-immutable.
Best practices
Trigger data fetching from componentDidMount instead of componentWillMount.
Trigger all side effects with an Redux action, instead of chaining promises directly in component code. Effect those changes using Redux middleware, like redux-saga. In your case, you could use redux-promise, create an action like
const action = ({type: FETCH_DATA, payload: axios.get(ENDPOINT, params)});
The redux-promise middleware handles the resolving, so that your reducer will only see the resolved value in the payload.
You're dispatching type 'FETCH_ALL' and catching case FETCH_ALL. One is a variable and one is a string.

If an action needs to use some current state, who should fetch it from store?

The title may not clear enough, please consider this example:
If I have a data table, which you can select multiple rows, and click action button like delete.
now in my actions.js:
(selectedRows is an array that contains the row indexes, getSelectedPostIds is a selector which will fetch and convert selectedRows to postIds)
import { getSelectedPostIds } from 'selectors'
export const deletePosts = () => (dispatch, getState) => {
// encapsulate the parameter `postIds` in action
const postIds = getSelectedPostIds(getState())
dispatch({ type: 'DELETE' })
deletePostsApi(postIds)
// .then(...)
// .catch(...)
}
is there any problem in this design? Or I should avoid using getState in an action and just pass postIds as a parameter to the action:
export const deletePosts = postIds => dispatch => {
dispatch({ type: 'DELETE' })
deletePostsApi(postIds)
// .then(...)
// .catch(...)
}
The only difference is that who should fetch the state (use the selector) from store, 1. action or 2. the component who will dispatch the action (via mapStateToProps).
I'm not sure about the approach 1, and the approach 2 will make my component contains a lot of props just because some actions need them (or maybe this is totally fine?).
thanks.
This might be a matter of taste. I usually like to access getState directly since, as you point out, avoids passing a lot of props. And by doing that the action is easier to integrate in different components (I just need to call it instead of additionally editing the mapStateToProps). Also, since in the end both ways are accessing the global store, the intended redux data flow is not compromised in any way.
You can use redux-thunk if you want to work with state in your action creators. :)
https://github.com/gaearon/redux-thunk
function yourActionCreator() {
// Redux-thunk will catch all action creators that return functions
return (dispatch, getState) => {
// u can use state here
const { counter } = getState();
if (counter % 2 === 0) {
return;
}
// Dispatch your action creator as you would normally do
dispatch(increment());
};
}

Navigation after form completion the react/redux way

I have an action that I call to save a Brand that looks like this:
export function createBrand(props) {
return function(dispatch) {
postData('brands', props)
.then(response => {
dispatch({
type: CREATE_BRAND_SUCCESS,
payload: response
});
browserHistory.push("/brands/" + response.data.id);
}).catch(err => {
dispatch({type: CREATE_BRAND_ERROR});
});
}
}
This is called from a component. My question is around the browserHistory.push("/brands/" + response.data.id); which takes the user to the edit page for the brand they just saved. Is this the appropriate way/place to do this? Should I be responding to the CREATE_BRAND_SUCCESS dispatch event in the component itself instead? If so, what would that look like?
There's nothing wrong with the approach you're taking. It looks like you're using redux-thunk. I don't think it's a best practice to respond to events in the component. The only ways to do that (that I can think of) would be to create some custom middleware for checking the action type, then calling a method on your component (please don't do this) or using your reducers to keep some state around in the component like responsesFromApi: [response1, response2].
The approach I like most is to use tools that let me kick off declarative effects in the reducer while keeping the reducer pure. Redux Loop and my own redux-funk enable this. I like this approach, because the answer to "what happens when this action is dispatched" can be found in one place (the reducers). And declarative effects are easier to test.
So the way you'd do this with redux-funk is:
// in component.js in mapDispatchToProps
dispatch({
type: REQUEST_CREATE_BRAND, ...brandObject
});
// in reducer
const requestCreateBrand = brandName => postData('brands', brandName).catch(() => {type: CREATE_BRAND_FAILURE})
const navigateToBrand = id => browserHistory.push(`/brands/${id}`)
...
case REQUEST_CREATE_BRAND:
call(action, [requestCreateBrand, [action.brandName]])
return {...state, isRequesting: true}
case CREATE_BRAND_SUCCESS:
call(action, [navigateToBrand, [id]])
return {...state, isRequesting: false, brands: {...state.brands, [action.brandId]: action.brand}
...
You can also create declarative effects using the call function in Redux Saga

Resources