How to receive the AsyncStorage items in FlatList? - reactjs

What I want to do is show the items saved in AsyncStorage in a FlatList.
savePosts = async () => {
try {
let post = {
postId: '1',
postTitle: 'This is an example',
}
const posts = await AsyncStorage.getItem('posts') || '[]';
posts = JSON.parse(posts);
posts.push(post);
AsyncStorage.setItem('posts', JSON.stringify(posts)).then(() => {
});
} catch(error) {
}
};

You can use state and initialise it in componentDidMount:
state = {
data: [],
}
componentDidMount() {
AsyncStorage.getItem('posts').then(data => this.setState({ data }));
}
<FlatList data={this.state.data} ...otherProps />
Alternatively, if you use Redux, you can call AsyncStorage.getItem anywhere and dispatch an action to populate the store, which the FlatList component can connect to and display the data.

Related

Trying to use react-admin transform function on <Create />

I'm new to react-admin and I am trying to build a custom image gallery input. it should show a modal with images (data is already fetched and stored in the redux) so the user can select one or more images (upon selection an action is dispatched to update the reducer's value) and I need these selected images ids in the transform function on <Create /> so I can add the required data before dataProvider method is called.
but I have a weird issue, that might be because of my lack of react knowledge. in the snippet below, I try to get the useReducers value and then add it to the form.
import React, { useReducer, useMemo, useEffect, useCallback } from 'react';
import { Create as Ra_create } from 'react-admin';
const ctxInitialValues = {};
const galleryCtx = React.createContext(ctxInitialValues);
const CreateWithGallery = (props) => {
const [selectedImages, dispatch] = useReducer((state, { type, payload }) => {
switch (type) {
case 'UPDATE_STATE':
return { ...payload };
case 'INIT_RECORD':
return {
...state,
[payload]: [],
};
default:
return state;
}
}, ctxInitialValues);
const updateSelection = (record, image, operation) => {
if (operation === 'add') {
let newState = {
...selectedImages,
[record]: [...selectedImages[record], image],
};
dispatch({
type: 'UPDATE_STATE',
payload: newState,
});
} else if (operation === 'remove') {
let newState = {
...selectedImages,
[record]: selectedImages[record].filter((item) => item.id !== image.id),
};
dispatch({
type: 'UPDATE_STATE',
payload: newState,
});
}
};
const transformPayload = (data) => {
let transformed = {
...data,
};
// but I get {} here
for (let record in selectedImages) {
transformed[record] = selectedImages[record].map((item) => ({
id: item.id,
}));
}
return transformed;
};
useEffect(() => {
console.log(selectedImages);
// I get fresh values here
}, [selectedImages]);
const initializeRecord = (record) => {
dispatch({
type: 'INIT_RECORD',
payload: record,
});
};
return (
<galleryCtx.Provider
value={{
selectedImages,
updateSelection,
initializeRecord,
}}
>
<Ra_create {...props} transform={transformPayload}>
{props.children}
</Ra_create>
</galleryCtx.Provider>
);
};
export { galleryCtx };
export default CreateWithGallery;
when I try to access the selectedImages values in the transform function I get {}, which is the initial state. I have tried using useCallback and useMemo to make sure the values are changed after each dispatch but it did not make any difference.
there's also a similar behavior in this question as well:
React Admin: how to pass state to transform
how can I use state in the transform function?
I ended up with setting the transform prop on the component (in custom toolbar):
const CustomToolbar = (props: any) => {
const transform = useCallback((data: any) => {
return {
...data,
files: something_from_state,
};
}, [something_from_state]);
const handleClick = () => {
};
return <Toolbar {...props}>
<SaveButton
handleSubmitWithRedirect={handleClick} transform={transform}/>
</Toolbar>
};
to fix this you can use transform prop on as explained in the react-admin docs. it is still unclear though, why we can't get state in the transform function on the or .

Not all the props that get passed down is rendered anyone have an idea as to why

After I recieved the data from firebase and store it into the post state and I try to pass each data to another component one one data is been sent to the prop anyone have.
Any idea as to why?
import React, { Component } from 'react';
import BlogPost from './BlogPost'
import firebase from '../../config/fbConfig'
class BlogList extends Component {
state = {
posts: []
}
componentDidMount() {
const db = firebase.firestore()
db.collection('posts').get().then(snapshot => {
snapshot.docs.forEach(post => {
let id = post.id
let postData = post.data()
postData['id'] = id
this.setState({
posts: [...this.state.posts, postData]
})
})
})
}
render() {
console.log(this.state.posts)
return (
<>
{this.state.posts ?
this.state.posts.map(post =>
<BlogPost post={post} key={post.id} />
)
: <h1>loading</h1>}
</>
);
}
}
export default BlogList;
The issue is in how you update the state, you must either use functional setState since you call setState within a loop and use this.state.data to update state, but state updates are not performed immediately but happen asynchronously
componentDidMount() {
const db = firebase.firestore()
db.collection('posts').get().then(snapshot => {
snapshot.docs.forEach(post => {
let id = post.id
let postData = post.data()
postData['id'] = id
this.setState(prev => ({
posts: [...prev.posts, postData]
}))
})
})
}
or even better update once all the data is available
componentDidMount() {
const db = firebase.firestore()
db.collection('posts').get().then(async snapshot => {
const data = snapshot.docs.map(post => {
let id = post.id
let postData = post.data()
postData['id'] = id
return postData;
});
this.setState(prev => ({
posts: [...prev.posts, ...data]
}));
})
}

React Native useEffect Hooks and React Navigation setParams doesn't update on loading

How come my React Navigation setParams doesn't update data on component mounted?
const NewScreen = props => {
const [extraInfo, setExtraInfo] = useState({
title: '',
description: '',
});
const [mediaArray, setMediaArray] = useState(null);
const [someData, setSomeData] = useState({});
const grabData = useCallback(() => {
return axios
.get('url')
.then(res => {
/* does some data grabbing for setSomeData and setMediaArray */
setSomeData({
data1,
data2,
data3,
});
setMediaArray(media);
})
.catch(err => console.log('axios catch err', err));
}, []);
/* ... more data grabbing for extraInfo */
useEffect(() => {
grabData();
props.navigation.setParams({
mutate: props.mutate,
title: extraInfo.title,
description: extraInfo.description,
newDataForAray: mediaArray,
...someData,
});
}, [grabData]);
return (
<KeyboardAvoidingView behavior="padding" style={styles.keyboardView}>
...
</KeyboardAvoidingView>
);
};
NewScreen.navigationOptions = props => {
const {mutate, title, description, newDataForAray} = props.navigation.state.params;
const handleSubmit = () => {
mutate({
variables: {
title,
description,
},
});
};
return {
headerRight: () => (
<TouchableOpacity
onPress={() => handleSubmit()}
style={{marginRight: 10}}>
<Text>Done</Text>
</TouchableOpacity>
),
};
};
const mutation = gql`
mutation someMutation(
...
) {
...
}
`;
export default graphql(mutation)(withNavigation(NewScreen));
So on loading, I'm able to get data through axios. But when component mounts, the data in prop.navigation.setParams doesn't get populated. If I add someData, mediaArray or extraInfo, into the array of the useEffect callback, it will infinitely loop, rerendering my component, but the data in navigation.state will finally populate.
I want to be able to pass the values into navigation state so I can use it in the header
With your current setup, grabData is going to pull your data but won't refresh someData, mediaArray, etc until the next render. But, your next render won't run since your useEffect's dependency array won't trigger.
You could use two useEffects with one reserved for grabData and other for props.nav. Better yet, update props.nav within grabData.
update reflecting comment
Media or somedata value doesn't change immediately. Thus, reference the same media or somedata pulled by your api to your setParams
const grabData = useCallback(() =>
axios
.get('url')
.then(res => {
/* does some data grabbing for setSomeData and setMediaArray */
const someData = {
data1,
data2,
data3,
};
setSomeData(someData);
setMediaArray(media);
const paramsTest = {
mutate: props.mutate,
title: extraInfo.title,
description: extraInfo.description,
newDataForAray: media,
...someData,
};
console.log({ paramsTest }); // used to confirm values provided to nav
props.navigation.setParams(paramsTest);
})
.catch(err => console.log('axios catch err', err)), [props.navigation.setParams]);

How to get props at the first rendering

I have a container component in which I get the ID and drop this ID into the function and the request goes, in principle, the props should come right away, but they are undefined. But when you re-enter the same component, the necessary props are shown.
Explain how to make props appear on the first render?
class View extends React.Component {
componentDidMount() {
let id = this.props.match.params.id;
this.props.GetProjData(id);
}
render() {
return <ProjView {...this.props}></ProjView>;
}
}
let mapStateToProps = state => {
return {
initialValues: {
NameProj: state.project.OneProject.NameProj,
Text: state.project.OneProject.Text,
target: state.project.OneProject.target,
startdate: state.project.OneProject.startdate,
enddate: state.project.OneProject.enddate
},
error: state.settings.error,
loading: state.settings.loading
};
};
My request
export const GetProjData = data => async (
dispatch,
getState,
{ getFirestore }
) => {
const firestore=getFirestore()
try {
await firestore
.collection("Projects")
.where("idProject", "==", data)
.get().then(snap => {
snap.forEach(doc => {
let project=doc.data()
console.log(doc.data());
dispatch({type:getOne,project})
});
})
} catch (err) {}
};
If I'm understanding the flow of your app correctly, you need to account for the renders between when you request your project data and when you receive the project data.
class View extends React.Component {
// constructor fires first so we might as well move it here
constructor(props) {
const id = props.match.params.id;
props.GetProjData(id);
}
render() {
// Your component will rerender before receiving the new data.
// We block the component from mounting so that initialValues
// gets set only when we have the data needed
if (this.props.initialValues && this.props.initialValues.NameProj) {
// A better way to do this would be to listen to a loading variable
// that gets updated when your request finishes
return <ProjView {...this.props} />;
}
return null; // or loading graphic
}
}

Fetch data from firebase database and componentDidMount

I have a problem fetching data from firebase with componentDidMount().
Fetching the data working fine but the problem is when it comes to lifecycle method. I console.log out the fetched data.
it fetched data when the component renders and after it fetched, the data become undefined and my render method appeared and disappeared in 0.001sec.
Do I need to store the props data into the state in order to not to disappear
Thank you for the help!
Action
export const fetchUserAddressbook = () => {
const { currentUser } = auth;
return dispatch => {
database
.ref(`users/${currentUser.uid}/shippingAddress`)
.on('value', snapshot => {
dispatch({
type: FETCH_SHIPPING_ADDRESS_SUCCESS,
payload: snapshot.val()
});
});
};
};
class
componentDidMount() {
this.props.fetchUserAddressbook();
}
render(){
return
(
<div>{this.renderAddressCard(this.props.shippingAddressBook)}</div>
)
const mapStateToProps = state => {
return {
shippingAddressBook: state.user.shippingAddressBook
};
}
renderMethod
renderAddressCard(shippingAddressBook) {
return _.map(shippingAddressBook, (value, uid) => {
return (
<Card header={value.companyName} />
);
});
}

Resources