Navigation after form completion the react/redux way - reactjs

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

Related

Do you need action creators when working with Redux hooks?

This is an odd question, but I haven't been able to find an explicit answer for it.
In my project I'm using Redux with new Redux hooks, useSelector and useDispatch. The setup is as standard as it gets:
Reducer:
const reducer = (state = defaultState, action) => {
switch (action.type) {
type SOME_ACTION:
return {
...state,
someVariable: action.payload
}
default:
return state
}
Synchronous actions:
export function someAction(payload) {
return {
type: SOME_ACTION
payload: payload
}
And in the components I dispatch actions using
dispatch({type: SOME_ACTION, payload: payload}).
Recently I've noticed that dispatching works just fine even if the action creator doesn't exist: as in, if the SOME_ACTION type is listed in the reducer switch operator, but there is no function someAction(payload).
The question is: is writing the action creators redundant in this case? What are the best practices when working with Redux hooks?
You can certainly use action object literals
dispatch({ type: 'MY_SUPER_AWESOME_ACTION });
But creating action creators allow you to reuse code more efficiently.
const mySuperAwesomeAction = () => ({ type: 'MY_SUPER_AWESOME_ACTION });
And now anywhere in your app where you need to dispatch this action you import the action and use
dispatch(mySuperAwesomeAction());
As apps grow and actions are used in multiple components if you were using object literals you would need to find/replace all instances of the action literal, and hope you didn't typo any of them. With the single action creator definition you need only update it in a single location.

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

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.

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.

Redux actions depending on/coupled to other actions

I am building a Redux application (my first) and am a little unclear about how much coupling is appropriate between actions.
My application has several forms whose values are serialized in the url.
For example, there is an input field for a particular search, and upon key-up a url parameter is updated. There are several other input fields that follow this pattern.
In my top-level index.js I have several blocks of code that look like this:
// Within the declaration of a high-level component
onForm1Change={(key, value) => {
// Listened to by "formValues" state cross-section reducer
store.dispatch({
type: actions.FORM1_CHANGE,
key: key,
value: value
});
// Listened to by "url" state cross-section reducer, leads to a url param update.
// Has it's own logic that is based upon the formValues state.
// Must run after FORM1_CHANGE finishes
store.dispatch({
type: actions.UPDATE_URL,
formValues: store.getState().formValues
});
}
}
Something about actions like UPDATE_URL doesn't feel right. This action doesn't stand on its own...it relies on other actions to be dispatched first.
Is this sort of coupling between actions a code smell? Are there any common techniques to de-couple/refactor these actions?
I think that's totally OK way of chaining synchronous actions. You can also use middleware like redux-thunk for this purpose to make it simpler to read (as you will store your actions dispatcher function as an action creater). Here is some article on this topic.
This is how i did it,
Defined a redux store middleware that will detect if any dispatched action has a queryString property, and update url with it.
import createHistory from "history/createBrowserHistory";
function queryStringMiddleware(history) {
return store => next => action => {
const { payload } = action;
if (payload.queryString) {
history.push({
search: queryString
});
}
next(action);
};
}
const history = createHistory();
const middlewares = [queryStringMiddleware(history)];
const store = configureStore({}, middlewares);
Then in your action:
const onForm1Change = (key, value) => {
store.dispatch({
type: actions.FORM1_CHANGE,
key: key,
value: value,
queryString: "?the=query"
});
};

Using connect and mapDispatchToProps with asynchronous calls

Consider the following example, and imagine the component is rendering based on the state of blogs:
class BlogList extends React.Component {
componentDidMount() {
getBlogPosts()
.then(blogs => this.props.dispatch({
type: 'LOAD_BLOGS',
blogs
}))
}
}
getBlogPosts calls an api, which returns a list of relevent blog posts. I would like to then update the redux store with the results of this call. The preceding code does this successfully, but I would like to pull this out into a mapDispatchToProps param to connect(), something like:
const mapDispatchToBlogListProps = (dispatch) => {
return {
loadBlogs: getBlogPosts()
.then(blogs => dispatch({
type: 'LOAD_BLOGS',
blogs
}))
}
};
const VisibleBlogList = connect(
mapStateToBlogListProps,
mapDispatchToBlogListProps
)(BlogList);
class BlogList extends React.Component {
componentDidMount() {
this.props.loadBlogs()
}
}
Is this good practice? Is there a better way? Can/should I use promises in the mapDispatch function?
The idea you're getting at is pretty common and you have to do something like this in order to handle requesting resources. I would suggest checking out the redux-thunk middleware though. You'll be able to handle your example and more complicated flows as well.
You'll likely soon run into cases where you need have multiple dispatches; for instance, you may need dispatch a loading action before a request, a success action if the request succeeds, and a fail action if the request returned in error, since all of these cases could affect your ui.

Resources