How to update the FlatList dynamically in react native? - javascript

Initially loading data from API to FlatList using setState and it loaded perfectly. But I have to perform some actions like create, update & delete of FlatList row. When I try to add new data to the FlatList, the data is not rendered in FlatList with an updated one, but In API it's updated.
How to re-render the flatlist after updating to the API and load the new data to FLatList?
Here is my code:
constructor(props) {
super(props);
this.state = {
faqs: [],
}
this.loadFaq();
};
To load the data to FlatList from the API:
loadFaq = async () => {
let resp = await this.props.getFaqGroup();
if (resp.faqs) {
console.log(resp.faqs)
this.setState({
faqs: resp.faqs,
// refresh: !this.state.refresh
})
}
};
To add new data to API:
createFaqGroup = async (name) => {
let resp = await this.props.createFaqGroup(name);
// console.log("resp", resp)
// this.setState({
// refresh: !this.state.refresh
// })
// this.forceUpdate();
this.closePanel();
}
FlatList code:
{this.state.faqs && <FlatList
extraData={this.state.faqs}
horizontal={false}
data={this.state.faqs}
contentContainerStyle={{ paddingBottom: 75 }}
renderItem={({ item: faqs }) => {
return <Card gotoQuestionList={this.gotoQuestionList} key={faqs._id} faqs={faqs} openPanel={(selectedFaq) => this.openPanel({ name: selectedFaq.name, id: selectedFaq._id })} deletePanel={(selectedFaq) => this.deletePanel({ name: selectedFaq.name, id: selectedFaq._id, isPublished: selectedFaq.isPublished })}></Card>
}
}
keyExtractor={(item) => item._id}
/>}
this.props.createFaqGroup function code:
export const createFaqGroup = (name) => {
const options = {
method: 'POST',
data: { "name": name },
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${store.getState().auth.info.token}`
}
};
return async (dispatch) => {
console.log('url::', options)
try {
let url = `${config.baseUrl}${config.faqUrl}`;
let resp = await axios(url, options);
console.log(resp.data)
return resp && resp.data ? resp.data : null;
} catch (error) {
alert(error)
if (error.response && error.response.status === 401) {
dispatch({
type: type.ERROR,
data: error.response.data
});
} else {
dispatch({
type: type.CREATE_FAQ_GROUP_ERROR,
error: error.message
});
}
}
};
}
Any help much appreciated pls...

Flatlist will update automatically when you set your state i.e by using this.setState() function, it means whenever any changes made to your state variable it will rerender your flatlist. if you still face the same problem remove your this.state.faqs && part, this looks unnecessary because there is no need to check if you are passing the empty array to faltlist or not, flatlist allows you to pas empty array as well, it will not give you any error.

I think you should load data again, after you add them, so you can modify your function createFaqGroup like this:
createFaqGroup = async (name) => {
let resp = await this.props.createFaqGroup(name);
this.loadFaq();
this.closePanel();
}

Try this:
createFaqGroup = async (name) => {
let resp = await this.props.createFaqGroup(name);
this.setState({faqs: [...this.state.faqs, name]})
this.closePanel();
}

Related

How to pass more parameters in useInfiniteQuery?

I am using React query useInfiniteQuery to get more data
const { data, isLoading, fetchNextPage, hasNextPage, error, isFetching } =
useInfiniteQuery("listofSessions", listofSessions, {
getNextPageParam: (lastPage, pages) => {
if (lastPage.length < 10) return undefined;
return pages.length + 1;
},
});
API requests:
const listofSessions = async ({ groupId, pageParam = 1 }) =>
await axios
.get(`${apiURL}/groups/allsessions`, {
params: {
groupId: 63,
page: pageParam,
},
})
.then((res) => {
return res.data.data;
});
I want to pass groupId to listofSessions API function like that:
const { data, isLoading, fetchNextPage, hasNextPage, error, isFetching } =
useInfiniteQuery("listofSessions", listofSessions({groupId}), ....
But I get an error
Missing queryFn
How can I solve this problem of passing multiple parameter values in useInfiniteQuery?
Does passing a new function work?
const listofSessions = async ({ groupId, pageParam = 1 }) =>
await axios
.get(`${apiURL}/groups/allsessions`, {
params: {
groupId: 63,
page: pageParam,
},
})
.then((res) => {
return res.data.data;
});
// pass a new function
const { data, isLoading, fetchNextPage, hasNextPage, error, isFetching } =
useInfiniteQuery("listofSessions", ({ pageParam = 1 }) => listofSessions({ groupId, pageParam}), {
getNextPageParam: (lastPage, pages) => {
if (lastPage.length < 10) return undefined;
return pages.length + 1;
},
});
Edit: Please include dependencies in the query key InfiniteQuery(["listofSessions", groupId, moreSearchParams], so that the cache is valid for the search parameters. Thanks #TkDodo for pointing it out and improving the answer
If it is possible to refer to groupId inside listofSessions that would be a simpler solution.

What is the best way to call a function and render a child component onClick in React?

I have the below code, I want to call a function and render a child component onCLick. What is the best way to achieve this?
import AddOrder from './AddOrder'
return (
<Button onClick={handleCheckout}>Checkout</Button>
)
const handleCheckout = () => {
<AddOrder />
fetch("http://localhost:5000/create-checkout-session", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
items: data?.getUser ? data.getUser.cart : cart,
email: currentUser ? currentUser.email : undefined,
}),
})
.then(async (res) => {
if (res.ok) return res.json();
const json = await res.json();
return await Promise.reject(json);
})
.then(({ url }) => {
window.location = url;
})
.catch((e) => {
console.error(e.error);
});
};
I tried making a new function called handleAll and adding it like this:
function handleAll(){
handleCheckout()
<AddOrder />
}
AddOrder.js:
function AddOrder() {
const d = new Date();
let text = d.toString();
const { currentUser } = useContext(AuthContext);
const { data, loading, error } = useQuery(queries.GET_USER_BY_ID, {
fetchPolicy: "cache-and-network",
variables: {
id: currentUser.uid
},
});
const [addOrder] = useMutation(queries.ADD_ORDER);
useEffect(() => {
console.log('hi')
})
if(error) {
return <h1> error</h1>;
}
if(loading) {
return <h1> loading</h1>;
}
if (data){
let newCart = []
for(let i=0; i< data.getUser.cart.length; i++){
newCart.push({quantity: data.getUser.cart[i].quantity, _id: data.getUser.cart[i]._id})
}
console.log(newCart)
addOrder({
variables: {
userId: currentUser.uid, status: 'ordered', createdAt: text, products: newCart
}
});
console.log("hello")
}
}
export default AddOrder;
This did not work either. When I reload this it add 3 copies of the same order to the mongodb collection. What is the right way to do this?

setState hook not updating after fetch with HTTP PUT

So I'm trying out basic todo app with edit and delete feature. I'm having problems with my edit feature. I have two main components in my app namely InputTodo for adding todo items and ListTodo which contains two additional subcomponents (TodoItem for each todo and EditTodo which shows an editor for a selected todo). Whenever the Edit Button inside a certain TodoItem is clicked, the EditTodo component is showed. When the Confirm button in EditTodo component is clicked, a PUT request will be sent to update the database (PostgreSQL in this case) through Node. After successfully sending this send request, I would like to re-render the list of TodoItem components. I'm doing this by fetching the updated list of values from the database through a different GET request then calling setState given the response from the GET request. However, the GET request's response doesn't reflect the PUT request done earlier. Thus, the app still renders the un-updated list of todos from the database.
Here are some code snippets
const ListTodo = (props) => {
const [todos, setTodos] = useState([]);
const [editorOpen, setEditorOpen] = useState(false);
const [selectedId, setSelectedId] = useState();
const getTodos = async () => {
console.log('getTodos() called');
try {
const response = await fetch("http://localhost:5000/todos");
const jsonData = await response.json();
setTodos(jsonData);
console.log(todos);
} catch (err) {
console.error(err.message);
}
console.log('getTodos() finished');
};
const editTodo = async description_string => {
console.log('editTodo() called');
try {
const body = { description: description_string };
const response = await fetch(
`http://localhost:5000/todos/${selectedId}`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body)
}
);
console.log(response);
await getTodos();
props.handleListModified();
} catch (err) {
console.error(err.message);
}
console.log('editTodo() finised');
}
const handleItemButtonClick = (button, row_key) => {
if (button === 'delete') {
deleteTodo(row_key);
setEditorOpen(false);
} else if (button === 'edit') {
setEditorOpen(true);
setSelectedId(row_key);
console.log(todos.filter(todo => { return todo.todo_id === row_key})[0].description);
}
};
const handleEditorButtonClick = async (button, description_string) => {
if (button === 'cancel') {
setSelectedId(null);
} else if (button === 'confirm') {
await editTodo(description_string);
}
setEditorOpen(false);
};
useEffect(() => {
console.log('ListTodo useEffect() trigerred');
getTodos();
}, [props.listModified]);
return(
<Fragment>
<table>
<tbody>
{todos.map( todo => (
<TodoItem
key={todo.todo_id}
todo_id={todo.todo_id}
description={todo.description}
handleClick={handleItemButtonClick} />
))}
</tbody>
</table>
{ editorOpen &&
<EditTodo
handleEditorButtonClick={handleEditorButtonClick}
description={todos.filter(todo => { return todo.todo_id === selectedId})[0].description}
selectedId={selectedId} /> }
</Fragment>
);
};
I guess that the problem is - In editTodo function, you are calling getTodos() function. But, you are not updating the state with the response you get. See if this helps.
const response = await fetch(
`http://localhost:5000/todos/${selectedId}`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body)
}
);
console.log(response);
setTodo(await getTodos()); // Update the state with the values from fetch

setState from .map inside componentDidMount()

I am trying to find a solution to using setState on mapped items inside componentDidMount.
I am using GraphQL along with Gatsby with many data items returned but require that on specific pathname is === to slug the state is updated in the component to the matching littleHotelierId.
propertyInit = () => {
const pathname = location.pathname;
return (
<StaticQuery
query={graphql`
query {
allContentfulProperties {
edges {
node {
id
slug
information {
littleHotelierId
}
}
}
}
}
`}
render={data => {
data.allContentfulProperties.edges.map(({ node: property }) => {
if (pathname === property.slug) {
!this.isCancelled &&
this.setState({
littleHotelierId: property.information.littleHotelierId
});
}
return null;
});
}}
/>
);
};
Then I am pulling this into componentDidMount as
componentDidMount() {
this.propertyInit();
}
not relevant but as reference this.isCancelled = true; is added to componentWillUnmount.
I don't receive any errors but if I console.log(littleHotelierId) I get nothing.
I did at first think that it may be because return is null so tried giving the map a const and returning as
render={data => {
data.allContentfulProperties.edges.map(({ node: property }) => {
if (pathname === property.slug) {
const littleHotelier =
!this.isCancelled &&
this.setState({
littleHotelierId: property.information.littleHotelierId
});
return littleHotelier;
}
});
}}
but this was unsuccessful too.
The Goal is for componentDidMount to map items returned in the GraphQL data as
componentDidMount() {
if (path1 === '/path-slug1') {
!this.isCancelled &&
this.setState({
littleHotelierId: 'path-id-1'
});
}
if (path2 === '/path-slug2') {
!this.isCancelled &&
this.setState({
littleHotelierId: 'path-id-2'
});
}
... // other items
}
I think the issue is that GraphQL is fetching data as asynchronous and this request not completed as componentDidMount() is called. If I console.log the data it is not returning anything to the console. How can I fix this?
I think you need to create some filtered data as a result of a map function. After you have filtered data you do setState({data: data}). It is not good to do multiple setState.
If your GraphQL returns promise then you can write something like the following:
componentDidMount() {
this.fetchData()
.then(data => {
const filteredData = data.filter(element =>
element.someProperty === propertyValue
);
this.setState({ data: filteredData });
})
}

How do you pass a react component's props down to options after apollo-client mutation?

How do you pass a react component's props down to options after apollo-client mutation?
I am using react with apollo-client. In a component I am trying to run a delete mutation after which I want to remove the item from the local store without doing a refetchQueries. In order to do so I've been using the options.update command.
In order to update the store, I need the parent ID of the object I'm trying to delete. It's available in the react component, I just need to find a way to pass it down to the options.update function.
const { fundId } = this.props;
const variables = { documentId: document.id };
const options = { variables }
this.props.deleteFundDocument(options)
.then( response => console.log("Document successfully deleted", response) )
.catch( e => console.log("Document not deleted", e) )
export default graphql(FundDocumentQL.deleteFundDocument, {name: 'deleteFundDocument', options: FundDocumentQLOptions.deleteFundDocument})
)(DocumentDisplay)
Here's what I pass in to the options from FundDocumentQLOptions as you can see I get the fundId from localStorage which is kind of hacky. I'd rather try and pass it down properly.
const deleteFundDocument = {
update: (proxy, {data: {deleteFundDocument}}) => {
try {
if (localStorage.getItem('documentViewerFundId')) {
const fundId = localStorage.getItem('documentViewerFundId');
let data = proxy.readQuery({query: FundDocumentQL.allFundDocuments, variables: {fundId: fundId}});
console.log('data.allFundDocuments 1', data.allFundDocuments);
// console.log('documentId', documentId);
console.log('variables.documentId', variables.documentId);
const newDocuments = data.allFundDocuments.filter( item => {
return item.id !== deleteFundDocument.id;
});
console.log('newDocuments', newDocuments);
data.allFundDocuments = [...newDocuments];
console.log('data.allFundDocuments 2', data.allFundDocuments);
proxy.writeQuery({query: FundDocumentQL.allFundDocuments, data, variables: {fundId: fundId}});
}
} catch (e) {
console.log(e);
}
}
};
I saw this example in the apollo-client docs:
https://www.apollographql.com/docs/react/basics/mutations.html#graphql-mutation-options-variables
export default graphql(gql`
mutation ($foo: String!, $bar: String!) {
...
}
`, {
options: (props) => ({
variables: {
foo: props.foo,
bar: props.bar,
},
}),
})(MyComponent);
And I saw this answer:
Apollo can't access queryVariables in update: after a mutation
Reading the other answer here Apollo can't access queryVariables in update: after a mutation
I realized I could pass fundId from this.props into the update function when I created the options object ahead of the mutation.
const { fundId } = this.props;
const variables = { documentId: document.id };
const options = {
variables: variables,
update: (proxy, { data: { deleteFundDocument } }) => FundDocumentQLOptions.deleteFundDocument(proxy, deleteFundDocument, fundId)}
this.props.deleteFundDocument(options)
.then( response => console.log('Document successfully deleted', response) )
.catch( e => console.log('Document not deleted', e) )
export default graphql(FundDocumentQL.deleteFundDocument, {name: 'deleteFundDocument'})(DocumentDisplay)
From FundDocumentQLOptions
const deleteFundDocument = (proxy, deleteFundDocument, fundId) => {
try {
let data = proxy.readQuery({query: FundDocumentQL.allFundDocuments, variables: {fundId: fundId}});
// console.log('data.allFundDocuments 1', data.allFundDocuments);
data.allFundDocuments = data.allFundDocuments.filter( item => {
return item.id !== deleteFundDocument.id;
});
// console.log('data.allFundDocuments 2', data.allFundDocuments);
proxy.writeQuery({query: FundDocumentQL.allFundDocuments, data, variables: {fundId: fundId}});
} catch (e) {
console.log(e);
}
};

Categories

Resources