I am trying to load an array I saved using AsyncStorage back into state but I cant seem to get it working. I am passing the array from AsyncStorage back into context and calling the load_state case.
function loadList() {
try {
const data = AsyncStorage.getItem('data')
console.log(data)
loadState(JSON.parse(data))
}
catch (error) {
console.log(error)
}
}
const loadState = dispatch => {
return (value) => {
dispatch({ type: 'load_state', payload: value})
}}
case 'load_state':
console.log(action.payload.value)
return [...state, ...action.payload.value]
Related
I am trying to get data from local memory using asyncStorage but there is one issue
useEffect( async () => {
try {
if(activemanagegroup !== null) {
var groupValue = JSON.stringify(activemanagegroup)
await AsyncStorage.setItem('managementGroup', groupValue)
}
var listValue = JSON.stringify(list)
await AsyncStorage.setItem('selectedList', listValue)
} catch (e) {
console.log('Failed to save data')
}
},[activemanagegroup, list])
useEffect(() => {
async function getData() {
try {
const managementGroupValue = await AsyncStorage.getItem('managementGroup')
const managedUsersList = await AsyncStorage.getItem('selectedList')
const activeManagementGroupSelected = managementGroupValue != null ? JSON.parse(managementGroupValue) : null
const activeList = managedUsersList != null ? JSON.parse(managedUsersList) : null
setActiveManagementGroup(activeManagementGroupSelected)
setNewList(activeList)
} catch (error) {
console.log('error getting data', error)
}
}
getData()
},[activemanagegroup])
the problem is selectedList updates a second later after managementGroup and due to that I end up getting old selectedList. How I can delay the call and make sure I get updated selectedList ?
Note: I am storing both these values once user presses a button.
I wouldn't recommend using AsyncStorage to retrieve the data more than once; once you have the data loaded initially, you should use React's built-in state management solutions to store the data instead of re-reading it from AsyncStorage.
Thus, I'd move your getItem calls to a separate useEffect that only runs once and updates the local React state:
const [managementGroupValue, setManagementGroupValue] = useState(null)
const [managedUsersList, setManagedUsersList] = useState(null)
useEffect(() => {
async function getData() {
try {
setManagementGroupValue(await AsyncStorage.getItem('managementGroup'))
setManagedUsersList(await AsyncStorage.getItem('selectedList'))
} catch (error) {
console.log('error getting data', error)
}
}
getData()
}, [])
and then use the managementGroupValue and managedUsersList variables to refer to that data instead of retrieving it from AsyncStorage each time.
I have a custom hook like so for getting data using useQuery. The hook works fine, no problem there.
const getData = async (url) => {
try{
return await axios(url)
} catch(error){
console.log(error.message)
}
}
export const useGetData = (url, onSuccess) => {
return useQuery('getData', () => getData(url), {onSuccess})
}
However, if I call this hook twice in my component it will only fetch data from the first call even with a different URL. (Ignore the comments typo, that's intentional)
The call in my component:
const { data: commentss, isLoading: commentsIsLoading } = useGetData(`/comments/${params.id}`)
const { data: forumPost, isLoading: forumPostIsLoading } = useGetData(`/forum_posts/${params.id}`)
When I console.log forumPost in this case, it is the array of comments and not the forum post even though I am passing in a different endpoint.
How can I use this hook twice to get different data? Is it possible? I know I can just call parallel queries but I would like to use my hook if possible.
Since useQuery caches based on the queryKey, use the URL in that name
const getData = async(url) => {
try {
return await axios(url)
} catch (error) {
console.log(error.message)
}
}
export const useGetData = (url, onSuccess) => {
return useQuery('getData' + url, () => getData(url), {
onSuccess
})
}
//........
const {
data: commentss,
isLoading: commentsIsLoading
} = useGetData(`/comments/${params.id}`)
const {
data: forumPost,
isLoading: forumPostIsLoading
} = useGetData(`/forum_posts/${params.id}`)
This is a simple question. How do I successfully update state object via react hooks?
I just started using hooks, and I like how it allows to use the simple and pure JavaScript function to create and manage state with the useState() function, and also, make changes that affect components using the useEffect() function, but I can't seem to make update to the state work!
After making a request to an API, it return the data needed, but when I try to update the state for an error in request and for a successful request, it does not update the state. I logged it to the browser console, but no change was made to the state, it returns undefined.
I know that I'm not doing something right in the code.
Here is my App component, Its a single component for fetching and updating:
import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';
export default function App() {
// Set date state
const [data,setData] = useState({
data: [],
loaded: false,
placeholder: 'Loading'
});
// Fetch and update date
useEffect(() => {
fetch('http://localhost:8000/api/lead/')
.then(response => {
if (response.status !== 200) {
SetData({placeholder: 'Something went wrong'});
}
response.json()
})
.then(result => {
console.log(data);
setData({data: result});
});
},[]);
return (
<h1>{console.log(data)}</h1>
);
}
ReactDOM.render(<App />, document.getElementById('app'));
There are a few things you can improve:
the react-hook useState does not behave like the class counterpart. It does not automatically merge the provided object with the state, you have to do that yourself.
I would recommend if you can work without an object as your state to do so as this can reduce the amount of re-renders by a significant amount and makes it easier to change the shape of the state afterwards as you can just add or remove variables and see all the usages immediately.
With a state object
export default function App() {
// Set date state
const [data,setData] = useState({
data: [],
loaded: false,
placeholder: 'Loading'
});
// Fetch and update date
useEffect(() => {
fetch('http://localhost:8000/api/lead/')
.then(response => {
if (response.status !== 200) {
throw new Error(response.statusText); // Goto catch block
}
return response.json(); // <<- Return the JSON Object
})
.then(result => {
console.log(data);
setData(oldState => ({ ...oldState, data: result})); // <<- Merge previous state with new data
})
.catch(error => { // Use .catch() to catch exceptions. Either in the request or any of your .then() blocks
console.error(error); // Log the error object in the console.
const errorMessage = 'Something went wrong';
setData(oldState=> ({ ...oldState, placeholder: errorMessage }));
});
},[]);
return (
<h1>{console.log(data)}</h1>
);
}
Without a state object
export default function App() {
const [data, setData] = useState([]);
const [loaded, setLoaded] = useState(false);
const [placeholder, setPlaceholder] = useState('Loading');
// Fetch and update date
useEffect(() => {
fetch('http://localhost:8000/api/lead/')
.then(response => {
if (response.status !== 200) {
throw new Error(response.statusText); // Goto catch block
}
return response.json(); // <<- Return the JSON Object
})
.then(result => {
console.log(data);
setData(data);
})
.catch(error => { // Use .catch() to catch exceptions. Either in the request or any of your .then() blocks
console.error(error); // Log the error object in the console.
const errorMessage = 'Something went wrong';
setPlaceholder(errorMessage);
});
},[]);
return (
<h1>{console.log(data)}</h1>
);
}
The correct way to update an Object with hooks it to use function syntax for setState callback:
setData(prevState => {...prevState, placeholder: 'Something went wrong'})
Following method will override your previous object state:
setData({placeholder: 'Something went wrong'}); // <== incorrect
Your final code should look like this:
.then(response => {
if (response.status !== 200) {
setData(prevObj => {...prevObj, placeholder: 'Something went wrong'});
}
return response.json()
})
.then(result => {
setData(prevObj => {...prevObj, data: result});
});
I need to do a dynamic action. In other words, it can be reused for differents actions.
I tried to create a function that loads type and payload, but an error appears.
I'm trying make this function works:
export function getData(url, type) {
const request = Server.get(url)
return (dispatch) =>
request.then((response) => {
dispatch({
type: type,
payload: response.data
})
}).catch(function (error) {
console.log(error)
});
}
But I got an error when I call this function this way:
export function getClientes() {
Actions.getData('ClientesEFornecedores', GET_CLIENTES)
}
It's showing:
Uncaught Error: Actions must be plain objects. Use custom middleware for async actions.
I'm Calling the getClientes() function this way:
function ClientesTable(props)
{
const dispatch = useDispatch();
const clientes = useSelector(({erpCliente}) => erpCliente.clientes.data);
useEffect(() => {
dispatch(Actions.getClientes());
}, [dispatch]);
How can I make an action be reusable?
Try something like this
export const getData=(url, type) =>async dispatch=>{
try{
const response = await Server.get(url);
dispatch({ type: type,payload: response.data })
} catch(err){
console.log(err)
}
}
getClientes function
export const getClientes=() => dbActions.getData('ClientesEFornecedores', GET_CLIENTES);
In fact I had almost succeeded.
All that remained was to return the function call.
This is the way that works:
export function getClientes() {
return dbActions.getData('ClientesEFornecedores', GET_CLIENTES)
}
The data source for my app only provides data in XML format.
I use axios to get the XML data. It ends up as a string in the data section of the result.
I have tried to use xml2js to convert it, but it just fires off a async job and returns, so I dont get the redux-promise middelware to work. The payload is nothing when the reducers sends the data to the component that should render it.
Not sure if this makes sense, but can I make the reducer wait for the new function call to return before sending the data on the the component?
action index.js
export function fetchData(jobid, dest) {
const url = `${DATA_URL}jobid=${jobid}&refdist=${dest}`;
const request = axios.get(url);
console.log(request);
return {
type: FETCH_DATA,
payload: request
}
}
my reducer
export default function (state = [], action) {
console.log(action);
switch (action.type) {
case FETCH_DATA:
console.log("pre");
parseString(action.payload.data, function (err, result) {
// Do I need some magic here??? or somewhere else?
console.dir(result);
});
return [action.payload.data, ...state];
}
return state;
}
you should change your action creator code, because axios is async. And dispatch action after receive data.
You don't need this logic in reducer.
For async actions you may use redux-thunk
export const fetchData = (jobid, dest)=>dispatch =>{
const url = `${DATA_URL}jobid=${jobid}&refdist=${dest}`;
const request = axios.get(url).then(res=>{
parseString(res, function (err, result) {
if(result){
dispatch({
type: FETCH_DATA,
data:result
})
}
if(err) throw err
});
}).catch(err=>console.error(error))
};
///clean reducer
export default function (state = [], action) {
switch (action.type) {
case FETCH_DATA:
return [...state, action.data ];
}
return state;
}
Also you may need to know about fetching process: loading, success , failure.Then action creator may looks like:
export const fetchData = (jobid, dest)=>dispatch =>{
const url = `${DATA_URL}jobid=${jobid}&refdist=${dest}`;
dispatch({
type: FETCH_DATA_REQUEST,
data:result,
isFetching:true
})
const request = axios.get(url).then(res=>{
parseString(res, function (err, result) {
if(result){
dispatch({
type: FETCH_DATA_SUCCESS,
data:result,
isFetching:false
})
}
if(err) throw err
});
}).catch(err=>{
dispatch({
type: FETCH_DATA_FAILURE,
err:err,
isFetching:false
})
console.error(error)
})
};