Where I should fetch async data? - javascript

I am fetching data using axios and then map state to props with redux but I have a problem. If I dispatch the action in componentDidUpdate() the action execute indefinitely and if I used the constructor(props) I get undefined value for props so where I should fetch the data ?
import React, { Component } from 'react'
import {connect} from 'react-redux'
import { getUserPosts } from '../../actions'
class UserPosts extends Component {
//UNSAFE_componentWillMount() {
//}
constructor(props) {
super(props);
console.log(props);
}
componentDidUpdate() {
//this.props.dispatch(getUserPosts(this.props.user_reducer.login?.user._id));
}
showUserPosts = (user) => (
Array.isArray(user.userPosts) ?
user.userPosts.map((item, i) => (
<tr key={i}>
<td>{i}</td>
<td>author</td>
<td>date</td>
</tr>
))
: null
)
render() {
let user = this.props.user_reducer;
//console.log(user.userPosts);
return (
<div>
<div className="user_posts">
<h4>Your reviews:</h4>
<table>
<thead>
<tr>
<th>Name</th>
<th>Author</th>
<th>Date</th>
</tr>
</thead>
<tbody>
{this.showUserPosts(user)}
</tbody>
</table>
</div>
</div>
)
}
}
function mapStateToProps(state) {
//console.log(state);
return {
user_reducer: state.user_reducer
}
}
export default connect(mapStateToProps)(UserPosts)
action:
export function getUserPosts(userId) {
const req = axios.get(`/api/user_posts?user=${userId}`)
.then(res => res.data);
return {
type: 'GET_USER_POSTS',
payload: req
}
}

componentDidMount() is the best placement for the call to fetch.
Here is an example implementation of the axios fetch from componentDidMount():
import React from 'react'
import ReactDOM from 'react-dom'
import axios from 'axios'
class UserPosts extends React.Component {
constructor(props) {
super(props)
// Initiate state with an empty array of user posts
this.state = { userPosts: [] }
}
componentDidMount() {
axios.get('http://api-url-here')
.then((response) => {
// Set the userPosts when data is received.
// render method will show user posts when state changes
this.setState({userPosts: response.data})
})
}
showUserPosts = (user) => (
Array.isArray(user.userPosts) ?
user.userPosts.map((item, i) => (
<tr key={i}>
<td>{i}</td>
<td>author</td>
<td>date</td>
</tr>
))
: null
)
render() {
let user = this.state;
//console.log(user.userPosts);
return (
<div>
<div className="user_posts">
<h4>Your reviews:</h4>
<table>
<thead>
<tr>
<th>Name</th>
<th>Author</th>
<th>Date</th>
</tr>
</thead>
<tbody>
{this.showUserPosts(user)}
</tbody>
</table>
</div>
</div>
)
}
}
ReactDOM.render(<UserPosts />, document.getElementById('root'))

Related

Using Axios with isLoaded

Goal:
Use axios instead of fetch in order to display the data in the table
Problem:
Somehow it doesn't work when I use axios in relation to 'isLoaded'
What part of the code am I missing?
Stackblitz:
https://stackblitz.com/edit/react-cntoqk?
Info:
Newbie in Reactjs
import React from 'react';
import './style.css';
import React, { Component } from 'react';
import axios from 'axios';
export default class App extends Component {
constructor() {
super();
this.state = {
isLoaded: false,
listData: {}
};
}
componentDidMount() {
/**
fetch('https://jsonplaceholder.typicode.com/comments?postId=1')
.then(results => results.json())
.then(data =>
this.setState({
isLoaded: true,
listData: data
})
)
.catch(err => console.log(err));
*/
axios
.get('https://jsonplaceholder.typicode.com/comments?postId=1')
.then(response =>
this.setState({
isLoaded: true,
listData: data
})
);
}
render() {
const { isLoaded } = this.state;
if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<>
<table className="table">
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Email</th>
</tr>
</thead>
<tbody>
{this.state.listData &&
this.state.listData.map(item => {
return (
<tr key={item.id.toString()}>
<td>{item.id}</td>
<td>{item.name}</td>
<td>{item.email}</td>
</tr>
);
})}
</tbody>
</table>
</>
);
}
}
}
As Floyd already pointed out, your code should look like this
axios
.get('https://jsonplaceholder.typicode.com/comments?postId=1')
.then(response =>
this.setState({
isLoaded: true,
listData: response.data
})
);

why different jsx element does not work in react component?

I have been working with code that uses table,
working code
export class BoardList extends Component {
static propTypes = {
boards: PropTypes.array.isRequired,
getBoards: PropTypes.func.isRequired,
deleteBoard: PropTypes.func.isRequired,
}
componentDidMount() {
this.props.getBoards();
}
render(){
this.props.boards.sort((boardA, boardB) => {return boardA.id - boardB.id })
const sortRow = this.props.boards.map(board => {
return (
<tr key={board.id}>
<td>{board.id}</td>
<td>{board.author}</td>
<td>{board.title}</td>
<td>{board.created}</td>
<td>{board.updated}</td>
<td>
<button className="btn btn-danger btn-sm" onClick={this.props.deleteBoard.bind(this, board.id)}>
Delete
</button>
</td>
</tr>
)
})
return (
<Fragment>
<table className="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>Author</th>
<th>Title</th>
<th>Created</th>
<th>Updated</th>
<th />
</tr>
</thead>
<tbody>
{this.props.boards.length > 0 && (sortRow)}
</tbody>
</table>
</Fragment>
)
}
}
const mapStateToProps = state => ({
boards: state.boards.boards
})
export default connect(mapStateToProps, {getBoards, deleteBoard})(BoardList)
However, if I use different element, it does not work.
export class BoardList extends Component {
constructor(props){
super(props)
this.createCard = this.createCard.bind(this)
}
static propTypes = {
boards: PropTypes.array.isRequired,
getBoards: PropTypes.func.isRequired,
deleteBoard: PropTypes.func.isRequired,
}
componentDidMount() {
this.props.getBoards();
}
createCard(board) {
return (
<div key={board.id}>
<div className="card text-white bg-primary mb-3" style="max-width:20rem">
<div className="card-header">{board.author}</div>
<div className="card-body">
<h4 className="card-title">{board.title}</h4>
<p className="card-text">{board.body}</p>
<img src={board.image} style="max-width:100px"/>
</div>
</div>
</div>
)
}
render() {
this.props.boards.sort((boardA, boardB) => { return boardA.id - boardB.id });
const cardBoard = this.props.boards.map(this.createCard);
return (
<Fragment>
<h2>Boards</h2>
{this.props.boards.length > 0 && (cardBoard)}
</Fragment>
)
}
const mapStateToProps = state => ({
boards: state.boards.boards
})
export default connect(mapStateToProps, {getBoards, deleteBoard})(BoardList)
I get Uncaught (in promise) TypeError: Cannot read property 'data' of undefined. That data is from the my redux action.
export const getBoards = () => (dispatch, getState) => {
axios
.get("api/boards/", tokenConfig(getState))
.then(res => {
dispatch({
type: GET_BOARDS,
payload: res.data
})
})
.catch(err => dispatch(returnErrors(err.response.data, err.response.status)));
}
}
I think both methods map the props, so there shouldn't be any issue, but since I am new to React, I must be missing something here.

Why it is giving me 'this.state.UserData.map' is not a function?

I am getting an error in my code 'this.state.UserData.map' is not a function . i want get list from the database using fetch. i think i am forgetting something.
please help me remove this error. thanks in advance.
Here is my complete code to show list...
import React from 'react';
import ReactDOM from 'react-dom';
export default class FetchedData extends React.Component{
constructor(props){
super(props);
this.state={ UserData:[] };
this.headers=[
{key:1,label:'Name'},
{key:2,label:'Department'},
{key:3,label:'Marks'},
];
}
componentDidMount(){
fetch("https://www.veomit.com/test/zend/api/fetch.php")
.then(response => {
return response.json();
})
.then(result => {
this.setState({
UserData:result
})
.catch(error => {
console.log(
"An error occurred while trying to fetch data from Foursquare: " +error
);
});
});
}
render(){
return(
<div>
<table className="table table-bordered">
<thead>
<tr>
{
this.headers.map(function(h) {
return (
<th key = {h.key}>{h.label}</th>
);
})
}
</tr>
</thead>
<tbody>
{
this.state.UserData.map(function(item){
return (
<tr>
<td>{item.name}</td>
<td>{item.department}</td>
<td>{item.marks}</td>
</tr>
);
})
}
</tbody>
</table>
</div>
);
}
}
````````
Please replace your code I hope it's working for you.
Thanks
import React from 'react';
import ReactDOM from 'react-dom';
export default class FetchedData extends React.Component{
constructor(props){
super(props);
this.state={ UserData:[] };
this.headers=[
{key:1,label:'Name'},
{key:2,label:'Department'},
{key:3,label:'Marks'},
];
}
componentWillMount() {
fetch("https://www.veomit.com/test/zend/api/fetch.php")
.then(response => {
return response.json();
})
.then(result => {
this.setState({
UserData: result
});
})
.catch(function(error) {
console.log(
"An error occurred while trying to fetch data from Foursquare: " +
error
);
});
}
render(){
return(
<div>
<table className="table table-bordered">
<thead>
<tr>
{
this.headers.map(function(h) {
return (
<th key = {h.key}>{h.label}</th>
);
})
}
</tr>
</thead>
<tbody>
{ this.state.UserData.length > 0 ?
this.state.UserData.map((item,index) => (
<tr key={index}>
<td>{item.name}</td>
<td>{item.department}</td>
<td>{item.marks}</td>
</tr>
))
) : (
<tr>
<td colspan="3">No record found.</td>
</tr>
)}
</tbody>
</table>
</div>
);
}
}
````````
The data is an object - you need to convert it to an array with Object.values:
UserData: Object.values(result)
response is not array. You need convert response server to array of object to use map
response must like this. You can tell backend services to change like this or you convert like this to your state
[{name: 'John Doe', department: 'CEO', marks: 'title' } , {....} ]

Change in state propagation to props stops at react-redux-connected component

I have the following hierarchy of components:
The state is kept in MyPayments component (it is local state - even though you can see connected components, I barely use Redux).
It has the following structure:
{
payments: [
{
amount: 400.00,
status: pending
//...
},
{
amount: 200.00,
status: approved
//...
}
]
}
The payments array is passed to the child component (connected ClientPayments) as a prop - you can see it on the screenshot above. I believe that the connected component passes it further down to the ClientPayments component. But...
At some point in time, after a successful AJAX request, the status property of one of the payments may change. When it does, I want to change how the payment is rendered inside the ClientPayments component. However, when I inspect the props of the ClientPayments component in React devtools, I can see that the changed payment still has the same status here. The Connect(ClientPayments) component though has its payments prop correctly updated.
MyPayments.js
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { camelCaseKeysDeep } from './Utils'
import ClientPayments from './ClientPayments'
class MyPayments extends Component {
constructor () {
super()
this.state = {
payments: [],
isLoading: false,
}
this.updatePaymentStatus = this.updatePaymentStatus.bind(this)
}
componentDidMount () {
this.setState({
isLoading: true,
})
axios.get(`/api/users/${this.props.userId}/payments`, {
params: {
includes: [
'bankAccount',
],
},
}).then(response => {
const payments = response.data
const camelCasedPayments = camelCaseKeysDeep(payments)
this.setState({
payments: camelCasedPayments,
isLoading: false,
})
}).catch((thrown) => {
console.log(thrown)
this.setState({
isLoading: false,
})
})
}
updatePaymentStatus(paymentId, newStatus) {
this.setState((prevState) => {
let payments = prevState.payments
const paymentIndex = _.findIndex(payments, (payment) => (payment.id === paymentId))
payments[paymentIndex].status = newStatus
return {
payments: payments
}
})
}
render () {
const {payments, isLoading} = this.state
const userId = this.props.userId
const expandedId = parseInt(this.props.match.params.id)
return (
<div>
<h2>My payments</h2>
<div className='panel panel-default'>
<ClientPayments payments={payments} isLoading={isLoading}
expandedId={expandedId} userId={userId} onPaymentStatusChange={this.updatePaymentStatus}/>
</div>
</div>
)
}
}
const mapStateToProps = state => {
return {
userId: state.user.id,
}
}
export default connect(mapStateToProps)(MyPayments)
ClientPayments.js
import React, { Component } from 'react'
import { Button, Table } from 'react-bootstrap'
import { LinkContainer } from 'react-router-bootstrap'
import { connect } from 'react-redux'
import Loader from './Loader'
import PaymentRow from './PaymentRow'
import withFileUpload from './withFileUpload'
import SingleUploader from './SingleUploader'
import BankAccountTable from './BankAccountTable'
import StatusIndicator from './StatusIndicator'
import PaymentStatusAlert from './PaymentStatusAlert'
class ClientPayments extends Component {
constructor (props) {
super(props)
this.SingleUploaderWithFU = withFileUpload(
SingleUploader,
'file',
)
this.handleSwiftCopyUploaded = this.handleSwiftCopyUploaded.bind(this)
}
handleSwiftCopyUploaded (paymentId) {
this.props.dispatch({
type: 'NOTIFY',
status: 'success',
message: 'A new SWIFT copy has been uploaded',
})
axios.put(`/api/payments/${paymentId}/status`, {
'status': 'pending',
}).then(() => {
this.props.onPaymentStatusChange(paymentId, 'pending')
})
}
render () {
const {payments, isLoading, expandedId} = this.props
return (
<Table responsive striped hover fill>
<thead>
<tr>
<th />
<th>Created</th>
<th>Amount</th>
<th>Bank</th>
<th>Actions</th>
</tr>
</thead>
{
payments.map((payment) => {
const storedSwiftCopy = payment.swiftCopyNameOrig !== null ? {
name: payment.swiftCopyNameOrig,
preview: payment.swiftCopyFullPath,
thumb: payment.swiftCopyThumbPath,
} : null
return (
<PaymentRow key={payment.id} payment={payment}
initiallyExpanded={expandedId === payment.id}>
<div>
<StatusIndicator status={payment.status}/>
<PaymentStatusAlert status={payment.status} rejectionMsg={payment.rejectionMsg}/>
<h4>Bank account details</h4>
<BankAccountTable bankAccount={payment.bankAccount}/>
<h4>Swift copy upload</h4>
<this.SingleUploaderWithFU initFile={storedSwiftCopy}
autoUpload
postUrl={`/api/payments/${payment.id}/swift-copy`}
onFileUploaded={() => this.handleSwiftCopyUploaded(payment.id)}/>
</div>
</PaymentRow>
)
})
}
{
isLoading ? (
<tbody>
<tr>
<td colSpan={5}>
<div className='vertical-spacer'>
<Loader />
</div>
</td>
</tr>
</tbody>
) : (
payments.length === 0 && (
<tbody>
<tr>
<td colSpan={5}>
<div className='vertical-spacer'>
<div>
<p className='text-center'>You have no payments yet.</p>
<p className='text-center'>
<LinkContainer to='/payments/new'>
<Button bsStyle='primary'>Receive one</Button>
</LinkContainer>
</p>
</div>
</div>
</td>
</tr>
</tbody>
)
)
}
</Table>
)
}
}
export default connect()(ClientPayments)
Why isn't the state change propagated? What can I do to fix it?
You can find some related topic here:
React: why child component doesn't update when prop changes
Also,
please try to print the data you get on componentShouldUpdate,
you can find it here:
https://facebook.github.io/react/docs/react-component.html#shouldcomponentupdate

React Js Form Not Displaying Output

I did an input for an offline application form using React and built a view to display the form, but the view seems to keep giving error, i can't seem to identify it
Here is the code for the view:
import React from 'react'
export default class GradeData extends React.Component {
constructor (props) {
super(props)
this.state = {grades: []}
this.schoolDb = this.props.schoolDb
}
componentDidMount () {
this.updateGrades()
this.schoolDb.changes({
since: 'now',
live: true
}).on('change', (change) => {
this.updateGrades()
}).on('error', (err) => {
console.error(err)
})
}
updateGrades () {
this.schoolDb.allDocs({include_docs: true}).then((res) => {
var grades = res.rows.map((row) => row.grade)
this.setState({grades})
})
}
render () {
return (
<div className='eidsr-data'>
<div className='eidsr-data__header'>
<h3 className='eidsr-data__title'>Grades Overview</h3>
</div>
<div className='table-list'>
<table>
<thead>
<tr>
<th>Student ID</th>
<th>Semester</th>
<th>Period</th>
</tr>
</thead>
<tbody>
{this.state.grades.map((grade) => <DataRow key={grade._id} grade={grade} {...this.props} />)}
</tbody>
</table>
</div>
</div>
)
}
}
class DataRow extends React.Component {
render () {
let {grade} = this.props
return (
<tr >
<td>{grade.GradeInfo['studentID']}</td>
<td>{grade.GradeInfo['semester']}</td>
<td>{grade.GradeInfo['period']}</td>
</tr>
)
}
}
Here is the code for the form:
import React from 'react'
import GradeInfo from 'GradeInfo'
import {setUsers, getUsers, filterUsers} from 'UsersApi'
import UserList from 'UserList'
export default class GradeForm extends React.Component {
constructor (props) {
super(props)
this.state = {
grade: getUsers()
}
this.submitInfo = this.submitInfo.bind(this)
}
componentDidUpdate () {
setUsers(this.state.grade)
}
submitInfo (event) {
event.preventDefault()
let gradeInfo = Object.assign({}, this.props.grade)
this.setState({
grade: [
...this.state.grade,
{gradeInfo}
]
})
this.props.clearCurrentGrade()
}
render () {
let {
grade,
edit,
updateGrade,
updateGradeState,
} = this.props
return (
<div className='row'>
<div className='columns large-centered large-12 medium-12'>
<div className='form'>
<div className='container'>
<form action='' onSubmit={this.submitInfo}>
<div className='student-form__container'>
<GradeInfo edit={edit} handleChange={updateGrade('GradeInfo')} {...grade.GradeInfo} />
<button className='button expanded' type='submit'>Save</button>
</div>
</form>
</div>
</div>
</div>
</div>
)
}
}

Categories