React, Redux - Fetch data after updating via api call - reactjs

I am using react with redux and typescript, Trying to add item from react via api call which returns back whether it is success or failed.
So I fetch data from componentDidMount
componentDidMount() {
this.props.dispatch(loadData());
}
And from the actions layer I add item like below
static addItem = (Item: IAddItemRequest): Promise<number> => {
return Promise.resolve(
AI.addItem(Item).then((ItemData) => {
return ItemData;
}).catch(error => {
return error;
}));
}
So how can I loadData after Adding an Item
Also AddItem doesn't change in the state

A possible soln can be opted using two actions, 1 that add item and second that load data.
export const loadData = (ItemData) => {
return {
type: LOAD_DATA,
payload: ItemData
};
};
export const addItemAction = (Item) => {
return dispatch => {
addItem(Item).then(ItemData => {
dispatch(loadData(ItemData));
});
};
};
Above both are inter linked redux actions and we have to call first action in react component's componentDidMount method as follow
componentDidMount() {
this.props.dispatch(addItemAction(Item));
}

Related

Making too many request await Axios (ReactJS)

On the outside it seems not an issue, but when I open the DevTools and then go to network tab. It shows that there are 500 requests made. So how can I refactor the code so this will not happens?
useEffect(() => {
const fetchData = async () => {
try {
const response = await axios.get(
"https://raw.githubusercontent.com/XiteTV/frontend-coding-exercise/main/data/dataset.json"
);
dispatch(getData(response.data));
console.log('input');
} catch (err) {
console.log(err);
}
};
fetchData();
}, [dispatch]);
with redux first create a function which will push your request data into redux state like that outside your react component
export const getMyData = () => {
return async (dispatch) => {
const response = await axios.get(
"https://raw.githubusercontent.com/frontend-coding-exercise/main/data/dataset.json"
);
dispatch(getData(response.data));
}
}
create a function that will extract data from redux. state is redux current state, ownProps is your component props, like <Component customProp={1}/>
const mapStateToProps = (state: any, ownProps: any) => {
return {
...ownProps,
myProps: state.user
};
};
In your connect Function pass the function which will store your data in redux state
export default connect(mapStateToProps, { getMyData })(MyReactComponent);
that way you'll be able to access your data via your component props and also access your getMyData function and also the redux state you mapped to props
props.getMyData();

Unnecessary refetch is triggered after mutation React Apollo

I'm building a small ToDo list app with React Apollo and GraphQL. In order to add a new ToDo item I click "Add" button that redirects me to a different URL that has a form. On form submit I perform a mutation and update the cache using update function. The cache gets updated successfully but as soon as I return to the main page with ToDo list, the component triggers an http request to get the ToDo list from the server. How do I avoid that additional request and make ToDoList component pull data from the cache ?
My AddToDo component:
const AddToDo = () => {
const { inputValue, handleInputChange } = useFormInput();
const history = useHistory();
const [addToDo] = useMutation(ADD_TODO);
const onFormSubmit = (e) => {
e.preventDefault();
addToDo({
variables: { title: inputValue },
update: (cache, { data: { addToDo } }) => {
const data = cache.readQuery({ query: GET_TODO_LIST });
cache.writeQuery({
query: GET_TODO_LIST,
data: {
todos: [...data.todos, addTodo],
},
});
history.push("/");
},
});
};
return (
...
);
};
And ToDoList component
const ToDoList = () => {
const { data, loading, error } = useQuery(GET_TODO_LIST);
if (loading) return <div>Loading...</div>;
if (error || !loading) return <p>ERROR</p>;
return (
...
);
};
Works as expected.
Why unecessary? Another page, new component, fresh useQuery hook ... default(?) fetchPolicy "cache-and-network" will use cached data (if exists) to render (quickly, at once) but also will make request to be sure current data used.
You can force "cache-only" but it can fail if no data in cache, it won't make a request.

Displaying notification after successful async redux action

I'm trying to figure out the best way to display a sweetalert message after a successful async action. So I have an ExcursionDetail component that allows you to book the excursion. Here is the simplified component:
class ExcursionDetails extends Component {
bookExcursion() {
const userId = jwt_decode(localStorage.getItem('token')).sub;
this
.props
.actions
.bookExcursion(userId, this.props.match.params.id);
}
render() {
....
<RaisedButton label="Book Excursion" onClick={e => this.bookExcursion()}/>
....
)
}
}
const mapStateToProps = (state, ownProps) => {
return {excursion: state.excursion}
}
const mapDispatchToProps = (dispatch) => {
return {
actions: bindActionCreators(ExcursionActions, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ExcursionDetails);
The action creator:
export const bookExcursion = (userId, excursionId) => {
return (dispatch, state) => {
dispatch(requestBookExcursions())
return ExcursionApi
.bookExcursion(userId, excursionId)
.then(resp => {
if (resp.ok) {
return resp
.json()
.then(payload => {
dispatch(bookExcursionsSuccess(payload.data));
})
}
}).catch(err => {
dispatch(bookExcursionsFailed(err));
})
}
}
What would be the best practice to then display the sweet alert notification? The options I thought of were:
Add a bookSuccess property that I can view if true or false in my ExcursionDetails component and if true call the sweetalert function.
Create notification specific actions and reducers and listen for it in my components. Only issue with this is I would need to have some sort of setTimeout after every notification call to clear the notification reducer and this seems a bit hacky.
call the sweet alert function within my reducer
pass a callback to the action creator
redux-thunk returns a promise; however even if the http call fails it will return a successful promise so this option doesn't seem viable.
I would and is using the first option that you mentioned.
I have created a new component and pass the redux store using connect. I check for it if the value is true on componentWillReceiveProps and set the state according and then you can display your sweetalert.
Well you can call it in the action creator.
You can use something like toastr.
Simple and clean.
export const bookExcursion = (userId, excursionId) => {
return (dispatch, state) => {
dispatch(requestBookExcursions())
return ExcursionApi
.bookExcursion(userId, excursionId)
.then(resp => {
if (resp.ok) {
return resp
.json()
.then(payload => {
dispatch(bookExcursionsSuccess(payload.data));
//Here is your notification.
toastr.success('Have fun storming the castle!', 'Miracle Max Says')
})
}
}).catch(err => {
dispatch(bookExcursionsFailed(err));
})
}
}

Redux how and where to merge objects of two asynchronous calls?

I am using thunk middleware and I have two asynchronous actions creators like following.
export const fetchObject = () => {
return dispatch => {
let action = fetchObjectRequest();
dispatch(action);
let url = "URL1";
let promise = axios.get(url)
.then(response => dispatch(fetchObjectSuccess(response.data)));
return handlingErrorsPromise(promise,
error => {
console.error(JSON.stringify(error));
dispatch(errorOccurred(action, error))
}
);
}
};
Let's assume I have Object1 and Object 2 endpoints, but the problem is Object1 is required by almost all components and I have to somehow merge all other objects with data from Object1.
Ex: Object2 contains peoples id and I have to attach them names from Object1.
Currently I am mapping my component properties to both objects, and I have if statements in render checking if all object are fetched. Like this:
class Peoples extends Component {
componentWillMount(){
this.props.fetchObject1();
this.props.fetchObject2();
}
render() {
let peoples = this.mergeObjects();
//rendering
}
mergeObjects = () => {
let isFetching = this.props.object1.isFetching ||
this.props.object2.isFetching;
if (isFetching) {
return {
isFetching,
json: []
};
}
let mergedJson = {...};
return {
isFetching,
json: mergedJson
};
};
}
const mapDispatchToProps = dispatch => {
return {
fetchObject1: () => dispatch(fetchObject1()),
fetchObject2: () => dispatch(fetchObject2())
}
};
const mapStateToProps = state => {
return {
object1: state.object1,
object2: state.object2
};
};
export default Peoples = connect(mapStateToProps, mapDispatchToProps)(Peoples);
Is there a more elegant way to merge one asynchronous object with others in my store?
I believe you could use the Promise.all api since axios returns a promise.
Promise.all([this.props.fetchObject1(), this.props.fetchObject2()]).then((data) => {
//do stuff with data
})
Update:
You can trigger an action in your view this.props.action.fetchObjects() that does the Promise.all or yield all[] if you're using redux-saga and then trigger another action that updates your store with both of them at once (merged or not). You can easily merge them in your view or even in the selector function.

Best conventions to use async/await on a react redux app

I'd like to know in which different ways can we use async/await in a React Redux (with thunk)
and if there are good conventions, what are they? I think it'll be nice to have:
In the container, not have to use the promise then(), as in this.props.myAction.then(...)
Can we NOT have the async keyword in the class method, but in the method body instead? For example:
async doSomething () {
const data = await this.props.myAction()
console.log(data)
}
// But maybe (it's failing for me, if the action is similar to my working action example *see below)
doSomething () {
const handler = async () => await this.props.myAction() // I guess the await here is redundant, but added to be clear to you
const data = handler()
console.log(data)
}
My working solution at the moment follows:
// MOCK data
const MOCK_DATA = [1, 2, 3, 4, 5]
// Action
export function myAction (payload) {
return async (dispatch) => {
const getter = () => {
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve({serverResponse: MOCK_DATA})
}, 1200)
})
return promise
}
try {
return await getter()
} catch (error) {
console.log(error)
}
}
}
// Container
class Foobar extends Component {
async doSomething () {
const data = await this.props.myAction()
}
render () {
return (
<div>
<button onClick={this.doSomething}>Do something!</button>
</div>
)
}
}
The problem with "await" is you are Blocking the event loop and with Thunk you have to handle the Promises & dispatcher directly.
There is an alternative to Thunk that is more easy to use. redux-auto
from the documantasion
redux-auto fixed this asynchronous problem simply by allowing you to create an "action" function that returns a promise. To accompany your "default" function action logic.
No need for other Redux async middleware. e.g. thunk, promise-middleware, saga
Easily allows you to pass a promise into redux and have it managed for you
Allows you to co-locate external service calls with where they will be transformed
Naming the file "init.js" will call it once at app start. This is good for loading data from the server at start
The idea is to have each action in a specific file. co-locating the server call in the file with reducer functions for "pending", "fulfilled" and "rejected". This makes handling promises very easy.
You example would look like this:
// MOCK data
const MOCK_DATA = [1, 2, 3, 4, 5]
// data/serverThing.js
export default function (data, payload, stage, result) {
switch(stage){
case 'FULFILLED':
return result.serverResponse;
case 'REJECTED':
const error = result;
console.log(error)
case 'PENDING':
default :
break;
}
return data;
}
export function action (payload) {
return Promise.resolve({serverResponse: MOCK_DATA})
}
// Container
import React from "react"
import actions from 'redux-auto'
import { connect } from 'react-redux'
class Foobar extends Component {
const loading = (true === this.props.data.async.serverThing) ? "loading..." : "";
render () {
return (
<div>
<button onClick={()=>actions.data.serverThing()}>Do something!</button> { loading }
</div>
)
}
}
const mapStateToProps = ( { data }) => {
return { data }
};
export default connect( mapStateToProps )(Foobar);
It also automatically attaches a helper object(called "async") to the prototype of your state, allowing you to track in your UI, requested transitions.

Resources