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
Related
I have this on the front end (react) right now.
import '../styles/TourPage.css';
import React, { Component } from 'react';
import axios from 'axios'
class TourPage extends Component {
constructor(props) {
super(props);
this.state = {
myData: []
}
}
componentDidMount() {
axios.get('/getResults')
.then( res => {
console.log("Res is: ", res.data)
this.setState({
myData: res.data
});
})
console.log("res.data", this.state.myData)
}
render() {
console.log("res.data", this.state.myData)
return (
<table id="customers">
<tr>
<th>siteLocation</th>
<th>Services</th>
<th>cnum</th>
</tr>
{this.state.myData.length > 0? this.state.myData.map((data, index) => (
<tr>
{/* <tr key={index}> */}
<td>{data.location}</td>
<td>{data.Services}</td>
<td>{data.cnum}</td>
<button onClick={this.click} disabled={this.state.isLoading}> Delete </button>
{/* {this.state.data} */}
{/* </tr> */}
</tr>
))
: "No Data Found"}
</table>
);
}
}
export default TourPage;
What I want to do, is on a button click, set data._id in the state, and then call Axios to post it to the Node.JS backend server, so I can update the database. Basically to delete the document. as you can see below, I tried with <a href> but that seems to be an HTML-specific thing. I also tried with the button, but I cannot figure it out. How can I do this?
I have refactored most of your code. You can pass id using an anonymous arrow function.
Do modify this to suit your needs.
import { render } from "react-dom";
import React, { Component } from "react";
import axios from "axios";
import "../styles/TourPage.css";
class TourPage extends Component {
constructor(props) {
super(props);
this.state = {
myData: [],
isLoading: true
};
}
componentDidMount() {
axios
.get("/getResults")
.then((res) => {
this.setState({
myData: res.data
});
})
.catch((error) => {
// Handle the errors here
console.log(error);
})
.finally(() => {
this.setState({
isLoading: false
});
});
}
deleteById = (id) => {
// You'll get the id here
// Delete by id code goes here
};
render() {
// You can handle the loader part here with isLoading flag. In this case No data found will be shown initially and then the actual data
let { myData, isLoading } = this.state;
return (
<table id="customers">
<tr>
<th>siteLocation</th>
<th>Services</th>
<th>cnum</th>
</tr>
{myData.length > 0
? myData.map(({ location, Services, cnum, _id }, index) => (
<tr key={index}>
<td>{location}</td>
<td>{Services}</td>
<td>{cnum}</td>
<button
onClick={() => this.deleteById(_id)}
disabled={isLoading}
>
Delete
</button>
</tr>
))
: "No Data Found"}
</table>
);
}
}
export default TourPage;
Can you try this ?
render() {
deleteDoc = (id) => {
await fetch('service_url', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: id} )
.then(async response => {
await response.json().then(data => {
console.log(data);
});
})
.catch(err => {
console.log(err)
})
}
return (
<table id="customers">
<tr>
<th>siteLocation</th>
<th>Services</th>
<th>cnum</th>
</tr>
{this.state.myData.length > 0 ? this.state.myData.map((data, index) => (
<tr>
<td>{data.location}</td>
<td>{data.Services}</td>
<td>{data.cnum}</td>
<button onClick={this.deleteDoc(data._id)} disabled={this.state.isLoading}> Delete </button>
</tr>
))
: "No Data Found"}
</table>
);
}
I have a form that is wrapped from app.js who receive props from there to update my inputs and buttons on the file cadastro.js, but when I click on edit register my button change to edit from my function getDerivedStateFromProps my input with description doesn't change. It just updates after I click two times on the button edit.
But if I debug on the console in the function getDerivedStateFromProps show me at the right time. What is the problem with my code?
App.js
import React, { Component } from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import Cadastrar from "./components/Cadastrar";
import Tabela from "./components/Tabela";
class App extends Component {
state = {
update: '',
idTamanhoEditar: ''
}
editarRegistro = (idRegistroEditarTabela) => {
this.setState({idTamanhoEditar: idRegistroEditarTabela})
}
updateTabela = (atualizarTabela) => {
this.setState({update: atualizarTabela})
}
render() {
return(
<div>
<Cadastrar atualizarTabela={this.updateTabela} editarFromParent={this.state.idTamanhoEditar}/>
<Tabela editarRegistro={this.editarRegistro} updateFromParent={this.state.update} />
</div>
)
}
}
export default App;
Cadastrar.js
import React, { Component } from 'react';
import './Cadastrar.css';
import axios from "axios";
class Cadastrar extends Component {
constructor(props) {
super(props);
this.state = {
tamanho: {
id: '',
descricao: '',
},
error: '',
sucess: '',
tipoAcao: 'Cadastrar'
};
this.atualizaDados = this.atualizaDados.bind(this);
this.cadastrar = this.cadastrar.bind(this);
}
atualizaDados(e) {
let tamanho = this.state.tamanho;
tamanho[e.target.name] = e.target.value;
this.setState({tamanho: tamanho});
}
cadastrar(e) {
const {tamanho} = this.state;
if(tamanho.descricao !== '') {
axios.post(`http://localhost/react-project/src/api/register.php`, { descricao: tamanho.descricao })
.then(res => {
if(res.data === 'sucess') {
this.setState({tamanho:{id:'', descricao: ''}})
//Tabela.atualizarItensTabela();
this.setState({sucess: 'Cadastro efetuado com sucesso!', error: ''})
this.props.atualizarTabela(true);
}
})
} else {
this.setState({error: 'Preencha o campo descrição!', sucess: ''})
}
e.preventDefault();
}
static getDerivedStateFromProps(props, state) {
if(props.editarFromParent !== state.tamanho.id ) {
console.log("Entrou");
state.tamanho.id = props.editarFromParent;
state.tipoAcao = 'Atualizar';
state = Cadastrar.consultarTamanho(state.tamanho.id, state);
}
return null;
}
static consultarTamanho(idTamanho, state) {
axios.post(`http://localhost/react-project/src/api/consult.php`, { id: idTamanho })
.then(res => {
if(res.data.descricao) {
state.tamanho.descricao = res.data.descricao;
}
})
return state;
}
render() {
return (
<div id='formulario-de-cadastro' className='container'>
<div className='page-header'>
<h2 className='titulo-cadastrar-tamanho'>Cadastrar Tamanho</h2>
</div>
<form onSubmit={this.cadastrar}>
<input type='hidden' name='id' value={this.state.tamanho.id} onChange={ this.atualizaDados } /><br/>
<div className='form-group'>
<label htmlFor='descricao'>Descrição</label>
<input type='text' className='form-control' name='descricao' id='descricao' onChange={ this.atualizaDados } value={this.state.tamanho.descricao} /><br/>
<button type='submit' className='btn btn-primary'>{this.state.tipoAcao}</button>
<button type='submit' className='btn btn-danger ml-1'>Cancelar</button>
</div>
</form>
{this.state.error && <p className='alert alert-warning'>{this.state.error}</p>}
{this.state.sucess && <p className='alert alert-success'>{this.state.sucess}</p>}
</div>
);
}
}
export default Cadastrar;
Tabela.js
import React, { Component } from 'react';
import axios from 'axios';
import './Tabela.css';
class Tabela extends Component {
constructor(props) {
super(props);
this.state = {
tamanhos: [],
tamanho: {
id: '',
descricao: ''
},
}
this.apagarTamanho = this.apagarTamanho.bind(this);
this.atualizarItensTabela = this.atualizarItensTabela.bind(this);
}
componentDidMount() {
this.atualizarItensTabela();
}
atualizarItensTabela() {
let url = 'http://localhost/react-project/src/api/consultAll.php';
fetch(url)
.then((r) => r.json())
.then((json) => {
this.setState({tamanhos: json});
});
}
apagarTamanho(e, idTamanho) {
e.preventDefault();
axios.post(`http://localhost/react-project/src/api/delete.php`, { id: idTamanho })
.then(res => {
if(res.data === 'sucess') {
this.atualizarItensTabela();
}
})
}
editarTamanho(e, idTamanho) {
this.props.editarRegistro(idTamanho);
e.preventDefault();
}
render() {
return (
<div className='container mt-5'>
{this.props.updateFromParent && this.atualizarItensTabela()}
<table id='tabela-tamanhos' className='table table-hover'>
<thead>
<tr>
<th scope="col">Código</th>
<th scope="col">Descrição</th>
<th scope="col">Ações</th>
</tr>
</thead>
<tbody>
{this.state.tamanhos.map(
tamanho=>
<tr key={tamanho.id} className='row-tamanho'>
<th scope="row">{tamanho.id}</th>
<td>{tamanho.descricao}</td>
<td>
<button className='btn btn-primary mr-1' onClick={(e)=>this.editarTamanho(e, tamanho.id)}>Editar</button>
<button className='btn btn-danger' onClick={(e)=>this.apagarTamanho(e, tamanho.id)}>Apagar</button>
</td>
</tr>
)}
</tbody>
</table>
</div>
);
}
}
export default Tabela;
You are not returning anything from getDerivedStateFromProps you have to return an object to update the state
getDerivedStateFromProps is invoked right before calling the render method, both on the initial mount and on subsequent updates. It should return an object to update the state, or null to update nothing.
Change thegetDerivedStateFromProps `` method with the below. Return an object rather mutating the state.
state = Cadastrar.consultarTamanho(state.tamanho.id, state);
if (props.editarFromParent !== state.tamanho.id) {
console.log("Entrou");
return {
tamanho: {
id: props.editarFromParent,
descricao: '',
},
error: '',
sucess: '',
tipoAcao: 'Atualizar'
}
}
return null;
And Call the side effect state = Cadastrar.consultarTamanho(state.tamanho.id, state); in componentDidUpdate.
If you need to perform a side effect (for example, data fetching or an animation) in response to a change in props, use componentDidUpdate lifecycle instead.
App.js
import React, { Component } from "react";
import "./App.css";
import Router from "./Router";
class App extends Component {
render() {
return (
<div className="App">
<div>
<h1>React-Redux Store</h1>
<h2>Welcome to the React Store</h2>
</div>
<Router />
</div>
);
}
}
export default App;
index.js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import "bootstrap/dist/css/bootstrap.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
import { Provider } from "react-redux";
import { createStore } from "redux";
import reducer from "./reducer";
import "../node_modules/font-awesome/css/font-awesome.min.css";
const store = createStore(reducer);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
ShopHome.js
when i open the page, it appears TypeError: Cannot read property 'items' of undefined, I guess its something wrong with mapStateToProps and cannot define the state. i Wonder if i did something wrong in the reducer
import React, { Component } from "react";
import { NavLink } from "react-router-dom";
import { connect } from "react-redux";
import { addToCart } from "./action_type";
class ShopHome extends Component {
handleClick = id => {
this.props.addToCart(id);
};
render() {
return (
<div>
<table className="table">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Price</th>
<th>
<NavLink to="/myCart" exact activeStyle={{ color: "green" }}>
my cart
</NavLink>
</th>
</tr>
</thead>
<tbody>
{this.props.items.map(item => {
return (
<tr key={item.id}>
<td>{item.name}</td>
<td>{item.description}</td>
<td>${item.price}</td>
<button to="/" onClick={() => this.handleClick(item.id)}>
add to cart
</button>
</tr>
);
})}
</tbody>
</table>
</div>
);
}
}
const mapStateToProps = state => {
return {
items: state.items
};
};
const mapDispatchToProps = dispatch => {
return {
addToCart: id => {
dispatch(addToCart(id));
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(ShopHome);
ShopCart.js
when i add the add quantity functionality it all works fine, but however after i added subtract quantity functionality it turns out says state is not defined(
TypeError: Cannot read property 'items' of undefined
Function.mapStateToProps [as mapToProps]
src/shopHome.js:47
44 | }
45 | const mapStateToProps = state => {
46 | return {
> 47 | items: state.items
48 | };
49 | };
50 | )
ShopCart.js
import React, { Component } from "react";
import { NavLink } from "react-router-dom";
import { connect } from "react-redux";
import { addQuantity } from "./action_type";
import { subtractQuantity } from "./action_type";
class ShopCart extends Component {
handleAddQuantity = id => {
this.props.addQuantity(id);
};
handleSubtractQuantity = id => {
this.props.subtractQuantity(id);
};
render() {
let addedItems = this.props.items.map(item => {
return (
<tr key={item.id}>
<td>{item.name}</td>
<td>
<NavLink to="/myCart">
<span>
<i
className="fas fa-plus-circle"
onClick={() => {
this.handleAddQuantity(item.id);
}}
></i>
</span>
</NavLink>
{item.quantity}
<NavLink to="/myCart">
<span>
<i
className="fas fa-minus-circle"
onClick={() => {
this.handleSubtractQuantity(item.id);
}}
></i>
</span>
</NavLink>
</td>
<td>${item.price}</td>
</tr>
);
});
return (
<div>
<table className="table">
<thead>
<tr>
<th>Item</th>
<th>Quantity</th>
<th>Price</th>
<th>
<NavLink to="/" exact activeStyle={{ color: "green" }}>
back to store
</NavLink>
</th>
</tr>
</thead>
<tbody>{addedItems}</tbody>
</table>
</div>
);
}
}
const mapStateToProps = state => {
return {
items: state.addedItems
};
};
const mapDispatchToProps = dispatch => {
return {
addQuantity: id => dispatch(addQuantity(id)),
subtractQuantity: id => dispatch(subtractQuantity(id))
};
};
export default connect(mapStateToProps, mapDispatchToProps)(ShopCart);
reducer.js
import { ADD_TO_CART, ADD_QUANTITY, SUBTRACT_QUANTITY } from "./action.js";
const initialState = {
items: [
{
id: 1,
name: "apple",
description: "Eat One Every Day, may keep the doctor away",
price: 12
},
{
id: 2,
name: "grape",
description: "Wine is great, but grapes is better",
price: 11
},
{
id: 3,
name: "pineapple",
description: "enjoy but don`t forget to peer first",
price: 8
}
],
addedItems: []
};
const reducer = (state = initialState, action) => {
if (action.type === ADD_TO_CART) {
let addedItem = state.items.find(item => item.id === action.id);
let existed_item = state.addedItems.find(item => item.id === action.id);
if (existed_item) {
addedItem.quantity += 1;
return {
...state
};
} else {
addedItem.quantity = 1;
return {
...state,
addedItems: [...state.addedItems, addedItem]
};
}
}
if (action.type === ADD_QUANTITY) {
let addedItem = state.items.find(item => item.id === action.id);
addedItem.quantity += 1;
return {
...state
};
}
if (action.type === SUBTRACT_QUANTITY) {
let addedItem = state.items.find(item => item.id === action.id);
if (addedItem.quantity === 1) {
let newItem = state.addedItems.filter(item => item.id !== action.id);
return {
...state,
addedItems: newItem
};
} else {
addedItem.quantity -= 1;
return {
...state
};
}
}
};
export default reducer;
action_type.js
import { ADD_TO_CART, ADD_QUANTITY, SUBTRACT_QUANTITY } from "./action";
export const addToCart = id => {
return {
type: ADD_TO_CART,
id
};
};
export const addQuantity = id => {
return {
type: ADD_QUANTITY,
id
};
};
export const subtractQuantity = id => {
return {
type: SUBTRACT_QUANTITY,
id
};
};
action.js
export const ADD_TO_CART = "ADD_TO_CART";
export const ADD_QUANTITY = "ADD_QUANTITY";
export const SUBTRACT_QUANTITY = "SUBTRACT_QUANTITY";
Hi everyone, I am new to react-redux, when i open the page, the page keep telling me that TypeError: Cannot read property 'items' of undefined in the ShopHome.js, i supppose its something wrong with declaring the state in the mapStateToProps function. can someone give me a hand?
I'm trying to store employee data in store using redux, but when I tried to access the store using mapStateToProps its returning empty array and it is getting called twice. The first time state has the vaue but second time it will return empty array when i inspected it
reducer/index,js:
import { AppConstants } from '../constants/actionTypes'
const initialState = {
employeeDetails: []
};
const rootReducer = (state = initialState, action) => {
switch (action.type) {
case AppConstants.ADD_EMPLOYEE:
let emplData = state.employeeDetails
let data = [
...emplData.slice(0, action.index),
action.payload,
...emplData.slice(action.index)
]
return {...state,
employeeDetails: data
}
default:
return state
}
}
export default rootReducer;
employee.js:
import React, { Component } from 'react';
import Input from '../components/Input'
import Button from '../components/Button'
import { addEmployee } from '../actions/index'
import { connect } from "react-redux";
class EmployeeForm extends Component {
constructor(props) {
super(props);
this.state = {
employee: [],
empName: "",
empId: "",
emailId: "",
empAge: "",
}
}
handleChange = (evt) => {
this.setState({
[evt.target.name]: evt.target.value
});
}
handleFormSubmit = () => {
debugger;
let employDet = {
empName: this.state.empName,
empId: this.state.empId,
emailId: this.state.emailId,
empAge: this.state.empAge
}
this.props.dispatch(addEmployee(employDet))
}
handleClearForm = () => {
}
handleDelete = (e) => {
}
render() {
debugger
let employeeDetails= this.props.employeeDetails
console.log("in render "+this.props.employeeDetails)
return (
<div>
<form className="container-fluid" >
<Input
inputType={"text"}
title={"Full Name"}
name={"empName"}
value={this.state.empName}
placeholder={"Enter your name"}
handleChange={this.handleChange}
/>{" "}
<Input
inputType={"text"}
title={"Email Id"}
name={"emailId"}
value={this.state.emailId}
placeholder={"Enter your Email Id"}
handleChange={this.handleChange}
/>{" "}
<Input
inputType={"text"}
title={"Employee Id"}
name={"empId"}
value={this.state.empId}
placeholder={"Enter your Employee Id"}
handleChange={this.handleChange}
/>{" "}
<Input
inputType={"number"}
name={"empAge"}
title={"Age"}
value={this.state.empAge}
placeholder={"Enter your age"}
handleChange={this.handleChange}
/>{" "}
<Button
action={this.handleFormSubmit}
type={"primary"}
title={"Submit"}
className="buttonStyle"
/>{" "}
<Button
action={this.handleClearForm}
type={"secondary"}
title={"Clear"}
className="buttonStyle"
/>{" "}
</form>
<br />
<table border="1" style={{ width: 400, paddingTop: 5 }}>
<thead>
<tr>
<th>Employee Name</th>
<th>Employee Id</th>
<th>Email Id</th>
<th>Age</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
{employeeDetails.map((emp, i) => {
return (
<tr key={i}>
<td>{emp.empName}</td>
<td>{emp.empId}</td>
<td>{emp.emailId}</td>
<td>{emp.empAge}</td>
{/* <td>
<button onClick={this.handleEdit} id={emp.id}>
Edit
</button>
</td> */}
<td>
<button onClick={this.handleDelete} id={emp.emailId}>
Delete
</button>
</td>
</tr>
);
})}
</tbody>
</table>
</div>
)
}
}
const mapStateToProps = state => {
const { employeeDetails } = state
return {
employeeDetails: employeeDetails
}
};
export default connect(mapStateToProps)(EmployeeForm)
store.js :
import { createStore, combineReducers, applyMiddleware } from
'redux';
import rootReducer from './reducers/index';
import thunk from 'redux-thunk';
const store = createStore(rootReducer,
applyMiddleware(thunk));
export default store;
action/index.js :
import {AppConstants} from '../constants/actionTypes'
export const addEmployee = (empData) => {
return dispatch => {
dispatch({ type: AppConstants.ADD_EMPLOYEE,
payload: empData
})
}
};
**ActionType.js **
export const AppConstants = {
ADD_EMPLOYEE : "ADD_EMPLOYEE",
}
The main problem is that you are using form submit button. The standard behaviour of submit button is to submit form and reload browser. Reload in single page app restarts the application from scratch, so you cannot use this standard submit button behaviour. Do not use submit button, e.g. change button type to 'button':
<button type={'button'} onClick={this.handleFormSubmit}>Submit</button>
The second problem might be in your reducer. When you are calling push on state.employeeDetails you are mutating it. Try to insert/push new item with immutable operation, redux has some topic on the problem in the documentation:
https://redux.js.org/recipes/structuring-reducers/immutable-update-patterns#inserting-and-removing-items-in-arrays
EDIT:
In your implementation of suggested redux update pattern you are inserting at specific index (but your action is not sending index now). When you want to just add item to the end (like push, but immutable) you can use array spread operator:
let data = [
...emplData,
action.payload
]
First, I want to load a JSON from my server; it will contain a list of objects. Then, I need to render these objects like a list. When a list item is clicked, the user should be redirected to a page that shows information about the clicked item. That information displayed should be fetched from another API call to my server.
Can someone guide me, please?
state = {
isLoading: true,
users: [],
error: null
};
fetchUsers() {
fetch(`http://localhost:3001/blog/view`)
.then(response => response.json())
.then(data =>
this.setState({
users: data,
isLoading: false,
})
)
.catch(error => this.setState({ error, isLoading: false }));
}
componentDidMount() {
this.fetchUsers();
}
render() {
const { isLoading, users, error } = this.state;
return (
<React.Fragment>
<h1 style={{textAlign: 'center'}}>My Blog</h1>
{error ? <p>{error.message}</p> : null}
{!isLoading ? (
users.map(user => {
const { _id, title, details,date } = user;
return (
<div className='blog'>
<div key={_id}>
<p>Name: {title}</p>
<p>Email Address: {details}</p>
<p >Email Address: {date}</p>
<hr className='banner-text hr' />
</div>
</div>
);
})
) : (
<h3>Loading...</h3>
)}
</React.Fragment>
);
}
}
Here is a sample project to demonstrate how you can use react and react-router together.
We first fetch a list of users from some api, and then display them as a list.
Using react-router, we add a link to each item so that when it's clicked, the page url changes
but page wont reload! these are internal links
Then again using react-router, we display different contents based on the url.
And at last, we have a UserPage component that when mounted, fetches the data for the specific user page and renders it.
Hope it is clear enough
This is a very good tutorial on react-router
And this is the official react tutorial
I strongly recommend that you take a look at them
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
App.js
import React, { Component } from 'react'
import {BrowserRouter as Router, Link, Route} from 'react-router-dom'
import User from "./User"
import UserPage from "./UserPage"
class App extends Component {
constructor() {
super()
this.state = {
users: [],
isLoading: false
}
}
componentDidMount() {
this.setState({ isLoading: true })
fetch('https://your/api/url')
.then(response => response.json())
.then(response => {
this.setState({
users: response,
isLoading: false
})
})
}
render() {
let content;
if (this.state.isLoading) {
content = <h1>Loading...</h1>
} else if (this.state.users.length > 0) {
content = this.state.users.map(u =>
<Link to={`/users/${u._id}`}>
<User key={u._id} user={u} />
</Link>
)
} else {
content = <h4>No users found!</h4>
}
return (
<Router>
<div>
<Route path="/users/:_id" component={UserPage}/>
<Route exact={true} path="/" render={() => content}/>
</div>
</Router>
)
}
}
export default App;
User.js
import React from 'react'
function User(props) {
const {title, details, date} = props.user;
return (
<div>
<p>Name: {title}</p>
<p>Email Address: {details}</p>
<p>Email Address: {date}</p>
<hr className='banner-text hr' />
</div>
)
}
export default User
UserPage.js
import React, {Component} from 'react'
class UserPage extends Component{
constructor(props) {
super(props)
this.state = {
isLoading: false,
data: '',
id: this.props.match.params._id
}
}
componentDidMount() {
this.setState({ isLoading: true })
fetch(`https://your/api/url/for/user/${this.state.id}`)
.then(response => response.json())
.then(response => {
this.setState({
data: response,
isLoading: false
})
})
}
render() {
return (
this.state.isLoading ?
(<h1>Loading page of user {this.state.id}...</h1>)
:
(
<div>
<p>{this.state.data}</p>
</div>
)
)
}
}
export default UserPage