React response data is undefined when rendered - javascript

In my react app I am fetching data from API, the response object is as below:-
posts {
total: 3,
data:[
{some data},
{some data},
{some data}
] }
The data is rendered using a map() function.
but whenever the page renders the data is displayed only once. After the first render when the page is re-rendered the data array is undefined (in console.log).
code for component:-
const function = () => {
const dispatch = useDispatch();
const { posts, isSuccess } = useSelector((state) => state.posts);
useEffect(() => {
dispatch(getPosts());
}, [dispatch]);
return (
<>
<div className="main-content">
{posts.data.map((post) => (
<Post
postId={post._id}
postSubject={post.subject}
/>
))}
</div>
</>
);
}
export default function;

You can try this before map function.
<React.Fragment>
{typeof object.data === typeof [] && (
<React.Fragment>
{object.data.map((obj, index) => {
return <React.Fragment>return some code .....</React.Fragment>;
})}
</React.Fragment>
)}
</React.Fragment>
I hope it's work

Related

how can I delete the element in react js

I want to create simple application with react js, which should show the users in the display and then when I click on the delete button, it should delete the following item, however I am having some errors.
import React, { useEffect, useState } from 'react'
const App = () => {
const [users, setUsers] = useState([])
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/users")
.then((response) => response.json())
.then((users) => {
setUsers(users);
})
}, [users]);
const deleteMe = () => {
setUsers(prevState => {
return prevState.filter(e => e.name)
})
}
return (
<>
{users.map((user) => {
return (
<>
<div> {user.name}
<button onClick={deleteMe}> Delete </button>
{/* <button onClick={}> Update </button> */}
</div>
</>
)
})}
</>
)
}
export default App
To remove the user, the callback (onClick) must have enough information to identify the user to be removed.
In this example, you have some options:
Remove by name. Only if the user names are unique:
const deleteMe = (userName) => {
setUsers(prevState => {
return prevState.filter(e => e.name !== userName)
})
}
return (
<>
{users.map((user) => {
return (
<>
<div> {user.name}
<button onClick={() => deleteMe(user.name)}> Delete </button>
{/* <button onClick={}> Update </button> */}
</div>
</>
)
})}
</>
)
Remove by the element itself. Only if the element isn't repeated in the array (the object itself):
const deleteMe = (user) => {
setUsers(prevState => {
return prevState.filter(e => e !== user)
})
}
return (
<>
{users.map((user) => {
return (
<>
<div> {user.name}
<button onClick={() => deleteMe(user)}> Delete </button>
{/* <button onClick={}> Update </button> */}
</div>
</>
)
})}
</>
)
Remove by the array index. Only if the state is an array, usually:
const deleteMe = (userIndex) => {
setUsers(prevState => {
return prevState.filter((e, i) => i !== userIndex)
})
}
return (
<>
{users.map((user, i) => {
return (
<>
<div> {user.name}
<button onClick={() => deleteMe(i)}> Delete </button>
{/* <button onClick={}> Update </button> */}
</div>
</>
)
})}
</>
)
See how a second parameter i was added to the map and filter functions. That is usually ignored, but it may be useful sometimes.
As this method may fail if the array is reordered of an element is added/removed between the render and the callback, I wouldn't recommend it unless there is no other alternative.
Look at the useEffect code. Because you have users as a dependency the effect will pick up any changes to that state. State changes, you make an API call, then update users, the effect gets called again on the next render, you update users in state, users gets updated again... etc.
It sounds like you just need an empty dependency array so that the effect is only called once when the component is rendered.
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/users")
.then((response) => response.json())
.then((users) => {
setUsers(users);
})
}, []);
try this , element get deleted and not refresh
import React, { useEffect, useState } from 'react';
const Example = () => {
const [users, setUsers] = useState([]);
useEffect(() => {
fetchData();
}, []);
const fetchData = async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/users');
const data = await response.json();
setUsers(data);
// .then()
// .then(users => {
// setUsers(users);
// });
};
const deleteMe = index => {
setUsers(prevState => {
console.log(prevState);
return prevState.filter((e, i) => i !== index);
});
};
return (
<div>
{users.map((user, i) => {
return (
<div>
{' '}
{user.name}
<button onClick={() => deleteMe(i)}> Delete </button>
{/* <button onClick={}> Update </button> */}
</div>
);
})}
</div>
);
};
export default Example;

TypeError: Cannot read property 'name' of undefined, unable to iterate through object array

I am a beginner in react please help me with this problem
when I am iterating through the array of objects that I have imported from category.json using map function, I am passing the props of object to my Category component, so when my category page is rendering it gives TypeError: Cannot read property 'name' of undefined.`
App.js
import React, { useState } from 'react';
import category from './data/categories.json';
import Categ from './Components/Categ';
function App() {
const [data, setData] = useState(category);
return (
<div className="App">
{data.map(item => (
<Categ key={item.id} name={item.name} desc={item.description} />
))}
</div>
);
}
export default App;
`
Categ.js
import React from 'react';
function Categ({ props }) {
console.log(props);
return (
<div>
<h1>{props.name}</h1>
<p>{props.desc}</p>
</div>
);
}
export default Categ;
category.json
[
{
"id": "fgsa2142fa",
"name": "Keyboards",
"description": "Buy different keyboard from any brand available"
},
{
"id": "xasgy42fa",
"name": "Headphones",
"description": "Find best-fit for your ears"
}
]
When your component first renders, your data object is undefined. You can solve this quite easily by a number of ways:
Optional chaining: ?. notation will call map function only if data is not falsy
function App() {
const [data, setData] = useState(category);
return (
<div className="App">
{data?.map(item => (
<Categ key={item.id} name={item.name} desc={item.description} />
))}
</div>
);
}
Conditional rendering:
function App() {
const [data, setData] = useState(category);
return (
<div className="App">
{data && data.map(item => (
<Categ key={item.id} name={item.name} desc={item.description} />
))}
</div>
);
}
or:
function App() {
const [data, setData] = useState(category);
return (
<div className="App">
{data ? data.map(item => (
<Categ key={item.id} name={item.name} desc={item.description} />
)) : <p> No data </p>}
</div>
);
}
Essentially, data && data.map and data?.map does the same thing here.
#Patryk's suggestion:
(data || []).map
The data variable renders as undefined for the first time, one way you can manage this as well is like the following:
function App() {
const [data, setData] = useState(category);
return (
<div className="App">
{!data ? <h1>Loading</h1> : data.map(item => (
<Categ key={item.id} name={item.name} desc={item.description} />
))}
</div>
);
}
Why undefined ?
You are passing the props as destruct format in Category. it does't have name key
You could pass the props as single param instead destruct
Do like this in category
function Categ(props) {
And also better validate the data before itreate
data && data.map

Iterating through Firebase database object and assining keys to react components

I am working in ReactJS and I have a database on Firebase called posts as a collection of objects. I am trying to iterate through the database objects and return a component with each assigned with one of the unique keys that firebase creates for the objects as props.
As a result example of what I am trying to achieve:
<Post
key={-MQcz3BC4lbKnvFe8Jl}
title={post.title}
type={post.type}
body={post.body}
clicked={() => this.postAnswerHandler( post.id )}
/>
<Post
key={-MQxVra23HwWb8ogRJZ}
title={post.title}
type={post.type}
body={post.body}
clicked={() => this.postAnswerHandler( post.id )}
/>
...and so on. Can anyone help with iterating through the firebase Data and assigning the keys to my React components?
Here is the current code I am using for this:
class Posts extends Component {
state = {
posts: []
}
componentDidMount () {
axios.get( 'https://blog-6d4da-default-rtdb.firebaseio.com/posts.json' )
.then( response => {
let posts = Object.values(response.data);
let key = Object.keys(response.data);
const updatedPosts = posts.map( post => {
return {
...post,
}
} );
this.setState( { posts: updatedPosts } );
} )
.catch( error => {
console.log( error );
// this.setState({error: true});
} );
}
render () {
let posts = <p style={{ textAlign: 'center' }}>Something went wrong!</p>;
if ( !this.state.error ) {
posts = this.state.posts.map( (post) => {
return (
<Post
key={post.key}
title={post.title}
type={post.type}
body={post.body}
clicked={() => this.postAnswerHandler( post.id )}
/>
// </Link>
);
} );
}
I think you're looking for:
let keys = Object.keys(response.data);
const updatedPosts = keys.map( key => {
return {
key, ...response.data[key],
}
} );
this.setState( { posts: updatedPosts } );
Use Object.entries()
axios.get( 'https://blog-6d4da-default-rtdb.firebaseio.com/posts.json' )
.then( response => {
this.setState( { posts: response } );
} )
render () {
let posts = <p style={{ textAlign: 'center' }}>Something went wrong!</p>;
if ( !this.state.error ) {
posts = Object.entries(this.state.posts).map( ([post, key]) => {
return (
<Post
key={key}
title={post.title}
type={post.type}
body={post.body}
clicked={() => this.postAnswerHandler( post.id )}
/>
// </Link>
);
} );
}

How return a const or function in primary function

I want to make a page with react components.
For that, I want to separate my code in several parts.
I want to create a page in my Layout function that uses various components like my MenuComponent.
However, I do not understand how to recover my MenuComponent in my Layout.
Thanks for your help.
function Menu () {
const [menuItems, setItems] = useState(null);
useEffect(() => {
getMenuItems().then(setItems);
}, []);
if (!menuItems) {
return null;
}
return (
<div>
{menuItems.data.root.children.map(menu => {
return (
<TreeItem key={menu.id} nodeId="1" label={menu.text} labelIcon={Label}>
{menu.children.map(child => {
return (
<TreeItem key={child.id} nodeId="1" label={child.text} labelIcon={Label}>
{console.log(child.text)}
</TreeItem>
);
})}
</TreeItem>
);
})}
</div>
);
}
export default function Layout() {
const classes = useStyles();
if (!isLogged()) {
return (
<div className="fondLogin" fullscreen>
<SnackbarProvider
maxSnack={1}
anchorOrigin={{
vertical: "top",
horizontal: "center"
}}
>
<Login onSuccess />
</SnackbarProvider>
</div>
);
}
return (
<div className="containerGeneral">
<InactivityManager />
<Menu />
</div>
);
}
Satif is completely right, if you are using a hooks approach, then you should make an async calls into useEffect hook.
Another thing, your component should always return something. If you want to prevent render just return null. In your case you are returning undefined.
function Menu () {
const [menuItems, setItems] = useState(null);
useEffect(() => {
getMenuItems().then(setItems);
}, []);
if (!menuItems) {
return null;
}
return (
<div>
{menuItems.children.map(menu => {
return (
<TreeItem key={menu.id} nodeId="1" label={menu.text} labelIcon={Label}>
</TreeItem>
);
})};
</div>
);
}
The menu component is wrong. You need to fetch a data in useEffect or componentDidMount methods.

React-apollo update not reloading after mutation if I transform data before the render()

I have wrapped for both Query and Mutations so I can globally handle the repeat actions that need to happen with each Query, Mutation. In the query I transform the data so I don't need to worry about all the nodes, edges, etc
I am using react-adopt to wrap all my query and mutations components into one render prop back on the view layer.
Works - Page will re-render once a mutation has taken place
<ApolloQuery>
export const ApolloQuery = ({
query: query,
render,
}) => {
return (
<Query query={query}>
{({ data }) => {
return (
<Fragment>
render(data)
</Fragment>
)
}}
</Query>
)
}
A Component
export default externalProps => {
return (
<QueryContainer {...externalProps}>
{({ someQueryData, aMutation }) => { //react-adopt render props
const { nestedData } = new apolloClass(someQueryData).start()
return (
<Grid container spacing={16}>
{nestedData.map((ticket, index) => (
{...Mutation button in here}
))}
</Grid>
)
}}
</QueryContainer>
)
}
Does not work - Page does not re-render but cache is updated with correct records
<ApolloQuery>
<Query query={query}>
{({ data }) => {
const transformedData = new apolloClass(data).start() //move transform into render
return (
<Fragment>
render(transformedData)
</Fragment>
)
}}
</Query>
A Component
export default externalProps => {
return (
<QueryContainer {...externalProps}>
{({ someQueryData: { nestedData }, aMutation }) => {
return (
<Grid container spacing={16}>
{nestedData.map((ticket, index) => (
{...Mutation button in here}
))}
</Grid>
)
}}
</QueryContainer>
)
}
So now, the page will not update after a mutation if I move the apolloClass to transform before the render of the query
Most likely you need to set refetchQueries or awaitRefetchQueries in the mutation options to force Apollo updating those queries and hence triggering a re-render.

Categories

Resources