Update component when the store changes - javascript

I'm trying to update a component based on store updates, the objective of that is, when I click on a table item, I want to update the form buttons to edit form, and Edit the table item.
My source code:
I have an action which updates currentUser. currentUser is the user I want to update
src/actions/user.js
export const updateCurrentUserSuccess = (currentUser) => {
return {
type: UPDATE_CURRENT_USER,
currentUser
}
}
export const updateCurrentUser = (id) => {
return (dispatch) => {
return axios.get(`${apiUrl}/users/${id}`)
.then(response => {
console.log(response.data.data)
dispatch(updateCurrentUserSuccess(response.data.data))
})
.catch(error => {
throw (error);
});
};
};
my currentUserReducer:
src/reducers/currentUserReducer.js
import { UPDATE_CURRENT_USER } from '../constants/ActionTypes';
const initialState = {
currentUser: [],
}
export default function userReducer(state = initialState, action) {
switch (action.type) {
case UPDATE_CURRENT_USER:
return action.currentUser;
default:
return state;
}
}
now my components:
my NewUser form:
src/components/NewUser.js
import React, { Component } from 'react';
import { Store } from '../store'
class NewUser extends Component {
state = {
id: '',
name: '',
cpfcnpj: '',
isEdit: false
};
componentDidMount(){
this.handleUserChange()
}
handleInputChange = e => {
this.handleUserChange();
this.setState({
[e.target.name]: e.target.value
});
};
handleSubmit = e => {
e.preventDefault();
if (!this.state.isEdit) {
if (this.state.name.trim() && this.state.cpfcnpj.trim()) {
this.props.onAddUser(this.state);
this.handleReset();
}
} else {
if (this.state.name.trim() && this.state.cpfcnpj.trim() && this.state.id !== '') {
this.props.onEdit(this.state);
this.handleReset();
}
}
};
handleReset = () => {
Store.getState().currentUser = []
this.setState({
id: '',
name: '',
cpfcnpj: '',
isEdit: false
});
};
handleUserChange() {
console.log('store', Store.getState().currentUser._id);
if (Store.getState().currentUser._id !== undefined) {
this.setState({
id: Store.getState().currentUser._id,
name: Store.getState().currentUser.name,
cpfcnpj: Store.getState().currentUser.cpfcnpj,
isEdit: true
});
}
}
render() {
return (
<div>
<form className="form-inline" onSubmit={this.handleSubmit}>
<div className="form-group margin-right">
<input
type="text"
placeholder="Name"
className="form-control"
name="name"
onChange={this.handleInputChange}
value={this.state.name}
/>
</div>
<div className="form-group margin-right">
<input
type="text"
placeholder="CPF/CNPJ"
className="form-control"
name="cpfcnpj"
onChange={this.handleInputChange}
value={this.state.cpfcnpj}>
</input>
</div>
<div className="form-group">
<button type="submit" className={this.state.isEdit ? "btn btn-success margin-right hidden" : "btn btn-success margin-right"}>
<span className="glyphicon glyphicon-plus" aria-hidden="true"></span>
Adicionar
</button>
<button type="submit" className={this.state.isEdit ? "btn btn-primary margin-right" : "btn btn-primary margin-right hidden"}>
<span className="glyphicon glyphicon-floppy-disk" aria-hidden="true"></span>
Salvar
</button>
<button type="button" className="btn btn-default margin-right" onClick={this.handleReset}>
<span className="glyphicon glyphicon-erase" aria-hidden="true"></span>
Limpar
</button>
</div>
</form>
</div>
);
}
}
export default NewUser;
my component User item:
***src/components/User.js***
import React from 'react';
export default ({ user: { name, cpfcnpj, _id }, onDelete, onEditUser }) => {
return (
<tr>
<th scope="row">{name}</th>
<td>{cpfcnpj}</td>
<td>
<button className="btn btn-warning btn-xs margin-right" type="button" onClick={() => onEditUser(_id)}>
<span className="glyphicon glyphicon-edit" aria-hidden="true"> </span>
Editar
</button>
<button className="btn btn-danger btn-xs margin-right" type="button" onClick={() => onDelete(_id)}>
<span className="glyphicon glyphicon-trash" aria-hidden="true"> </span>
Excluir
</button>
</td>
</tr>
);
};
now my smart components:
src/containers/UserList.js
import React from 'react';
import { connect } from 'react-redux';
import User from '../components/User';
import { deleteUser, updateCurrentUser } from '../actions/user';
import NewUser from '../components/NewUser';
function UserList({ users, onDelete, onEditUser }) {
if (!users.length) {
return (
<div className="margin-top">
No Users
</div>
)
}
return (
<div className="margin-top">
<table className="table table-striped">
<thead>
<tr>
<th scope="col">Nome</th>
<th scope="col">CPF/CNPJ</th>
</tr>
</thead>
<tbody>
{users.map(user => {
return (
<User user={user} onDelete={onDelete} onEditUser={onEditUser} key={user._id} />
);
})}
</tbody>
</table>
</div>
);
}
const mapStateToProps = state => {
return {
users: state.users
};
};
const mapDispatchToProps = dispatch => {
return {
onDelete: id => {
dispatch(deleteUser(id));
},
onEditUser: (id) => {
dispatch(updateCurrentUser(id))
}
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(UserList, NewUser);
src/containers/CreateUser.js
import { connect } from 'react-redux';
import { createUser, updateUser } from '../actions/user';
import NewUser from '../components/NewUser';
const mapDispatchToProps = dispatch => {
return {
onAddUser: user => {
dispatch(createUser(user));
},
onEdit: (id, name, cpfcnpj) => {
dispatch(updateUser(id, name, cpfcnpj))
}
};
};
export default connect(
null,
mapDispatchToProps
)(NewUser);
src/App.js
import React, { Component } from 'react';
import CreateUser from './containers/CreateUser';
import UserList from './containers/UserList';
import './css/main.css'
class App extends Component {
render() {
return (
<div className="container">
<h1 className="styles-app">Usuários</h1>
<div className="row styles-app">
<div className="col-md-12">
<CreateUser />
</div>
<div className="col-md-12">
<UserList />
</div>
</div>
</div>
);
}
}
export default App;

Here is something you might try. Connect your NewUser.js to the store.
import { connect } from 'react-redux;
export default connect(mapStateToProps)(NewUser);
Then map your currentUser state to props.
const mapStateToProps = state => {
return {
currentUser: state.currentUser
};
};
In your currentUserReducer
initialState = {
//Assuming these are the only values in response
id: '',
name: '',
cpfcnpj: '',
isEdit: false
};
export default function userReducer(state = initialState, action) {
switch (action.type) {
case UPDATE_CURRENT_USER:
return {
...state,
id: action.currentUser.id,
name: action.currentUser.name,
cpfcnpj: action.currentUser.cpfcnpj,
isEdit: true
};
default:
return state;
}
}
You should have access to the current user object now in props.
Then in your input value field
value={this.props.currentUser.name}
value={this.props.currentUser.cpfcnpj}
You may also need to do a check to see if these values have been updated. Also, not sure if the placeholder text might interfere.
Hope this gets you closer to the solution.
Edit
In the case of clearing props, you might just add another action to do so.
In your actions for currentUser:
export const clearUserData = () => {
return {
type: CLEAR_USER_DATA,
}
}
And in your reducer:
export default function userReducer(state = initialState, action) {
switch (action.type) {
case UPDATE_CURRENT_USER:
return {
...state,
id: action.currentUser.id,
name: action.currentUser.name,
cpfcnpj: action.currentUser.cpfcnpj,
isEdit: true
};
case CLEAR_USER_DATA:
return {
...state,
id: '',
name: '',
cpfcnpj: '',
isEdit: false
};
default:
return state;
}
}
Add the clearUserData action to execute after you submit your edits and it should reset your reducer. You might even be able to just do
return {
...state,
initialState
};

Related

TypeError: state is not iterable

I am new to React js, and I am trying to create a contact list. Where you can add contacts, update, and delete contacts from your contact list. However, I am trying to initialize the state of my contact list but when I run it, it displays the contact list screen without the "initialState" of the contact list. Then when I try to add a contact it then throws a type error screen(state is not iterable). How do I fix this issue?
contactReducer.js:
const initialState = [
{ id: 0, name: "Raman Sharma", Level: " 20", Progress: "Master" },
{ id: 1, name: "Test Name", Level: " 25", Progress: "Master" },
];
export const contactReducer = (state = initialState , action) => {
switch (action.type) {
case "ADD_CONTACT":
state = [...state, action.payload];
return state;
case "DELETE_CONTACT":
const contactFilter = state.filter((contact) =>
contact.id === action.payload ? null : contact
);
state = contactFilter;
return state;
case "UPDATE_CONTACT":
const contactUpdate = state.filter((contact) =>
contact.id === action.payload.id
? Object.assign(contact, action.payload)
: contact
);
state = contactUpdate;
return state;
case "RESET_CONTACT":
state = [{ name: null, Level: null, Progress: null }];
return state;
default:
return state;
}
};
./client/AddContact/index.js
import React, { useState } from "react";
import { connect } from "react-redux";
import { useHistory } from "react-router";
import { toast } from "react-toastify";
const AddPost = ({ contacts, addContact }) => {
const [name, setName] = useState("");
// const [Level, setLevel] = useState("");
// const [Progress, setProgress] = useState("");
const history = useHistory();
const handleSubmit = (e) => {
e.preventDefault();
// const checkContactLevelExists = contacts.filter((contact) =>
// contact.Level === Level ? contact : null
//);
// const checkContactProgressExists = contacts.filter((contact) =>
// contact.Progress === Progress ? contact : null
//);
if ( !name) {
return toast.warning("Please fill in field!!");
}
// if (checkContactLevelExists.length > 0) {
// return toast.error("This Level already exists!!");
// }
// if (checkContactProgressExists.length > 0) {
// return toast.error("This Progress number already exists!!");
// }
const data = {
id: contacts.length > 0 ? contacts[contacts.length - 1].id + 1 : 0,
// Level,
name,
// Progress,
};
addContact(data);
toast.success("Player Added successfully!!");
history.push("/");
};
return (
<div className="container-fluid">
<h1 className="text-center text-dark py-3 display-2">Add Friend</h1>
<div className="row">
<div className="col-md-6 p-5 mx-auto shadow">
<form onSubmit={handleSubmit}>
<div className="form-group">
<input
className="form-control"
type="text"
placeholder="Full name"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</div>
{/* <div className="form-group">
<input
className="form-control"
type="Level"
placeholder="Level"
value={Level}
onChange={(e) => setLevel(e.target.value)}
/>
</div> */}
{/* <div className="form-group">
<input
className="form-control"
type="number"
placeholder="Progress"
value={Progress}
onChange={(e) => setProgress(e.target.value)}
/>
</div> */}
<div className="form-group">
<input
className="btn btn-block btn-dark"
type="submit"
value="Add Student"
/>
</div>
</form>
</div>
</div>
</div>
);
};
const mapStateToProps = (state) => ({
contacts: state,
});
const mapDispatchToProps = (dispatch) => ({
addContact: (data) => {
dispatch({ type: "ADD_CONTACT", payload: data });
},
});
export default connect(mapStateToProps, mapDispatchToProps)(AddPost);
home.js
import React from "react";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
const Friends = ({ contacts, deleteContact }) => {
return (
<div className="container">
<div className="row d-flex flex-column">
<Link to="/add" className="btn btn-outline-dark my-5 ml-auto ">
Add Friend
</Link>
<div className="col-md-10 mx-auto my-4">
<table className="table table-hover">
<thead className="table-header bg-dark text-white">
<tr>
<th scope="col">Id</th>
<th scope="col">Name</th>
<th scope="col">Level</th>
<th scope="col">Progess</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
{contacts.length > 0 ? (
contacts.map((contact, id) => (
<tr key={id}>
<td>{id + 1}</td>
<td>{contact.name}</td>
<td>{contact.Level}</td>
<td>{contact.Progress}</td>
<td>
<Link
to={`/edit/${contact.id}`}
className="btn btn-sm btn-primary mr-1"
>
Edit
</Link>
<button
type="button"
onClick={() => deleteContact(contact.id)}
className="btn btn-sm btn-danger"
>
Remove
</button>
</td>
</tr>
))
) : (
<tr>
<th>No contacts found</th>
</tr>
)}
</tbody>
</table>
</div>
</div>
</div>
);
};
const mapStateToProps = (state) => ({
contacts: state,
});
const mapDispatchToProps = (dispatch) => ({
deleteContact: (id) => {
dispatch({ type: "DELETE_CONTACT", payload: id });
},
});
export default connect(mapStateToProps, mapDispatchToProps)(Friends);
The initial state's an object with a contactReducer property that is the array of contacts.
I'm certain you've just specified incorrect default state value.
Instead of
state = { contactReducer: initialState }
You likely want
state = initialState
All the reducers will now correctly specify/update state as an array.
Since the contactReducer reducer was combined with another reducer and provided to the store, this nests the state slice under the key you combined them with.
const reducer = combineReducers({
login: authReducer,
contactInfo: contactReducer, // <-- this
});
Instead of accessing via just state.X it is now state.contactInfo.X.
const mapStateToProps = (state) => ({
contacts: state.contactInfo,
});
However, there might be an issue in delete contact case in the use of filter.
Callback passed to a filter should return true or false only based on which it will be included or not in resultant array, instead you seem to have returned null or object.
Corrected:
case "DELETE_CONTACT":
const contactFilter = state.filter((contact) =>
contact.id !== action.payload
);
state = contactFilter;
return state;
The above filter will return true if the id's are not equal, which means it will be considered or false which means it will not be included in the resultant array
Additionally, in case of Update, you need to replace filter with map function as below:
case "UPDATE_CONTACT":
const contactUpdate = state.map((contact) =>
contact.id === action.payload.id
? {...contact, ...action.payload}
: contact
);
state = contactUpdate;
return state;
Furthermore, i would changes the contactReducer.js as below:
const initialState = { contacts: [
{ id: 0, name: "Raman Sharma", Level: " 20", Progress: "Master" },
{ id: 1, name: "Test Name", Level: " 25", Progress: "Master" },
]};
export const contactReducer = (state = initialState , action) => {
switch (action.type) {
case "ADD_CONTACT":
return {contacts: [...state.contacts, action.payload]};
case "DELETE_CONTACT":
return {contacts: state.contacts.filter((contact) =>
contact.id !== action.payload
)};
case "UPDATE_CONTACT":
return {contacts: state.contacts.map((contact) =>
contact.id === action.payload.id
? {...contact, ...action.payload}
: contact
)};
case "RESET_CONTACT":
return {contacts: [{ name: null, Level: null, Progress: null }]};
default:
return state;
}
};

How to dispatch async action in Redux using Redux Thunk: TypeError: Cannot read property 'loading' of undefined

I have set everything in Redux side and I see every action in Redux Devtool. It works all perfect. The problem occurs when I want to dispatch action in React Component. In login component I want to dispatch action, wait its response then depending on response redirect it to a page or show errors. Here are my codes:
userActions.js
import axios from "axios";
import {
LOGIN_USER,
LOGIN_USER_SUCCESS,
LOGIN_USER_FAILED,
} from "./types";
const loginUserRequest = () => {
return {
type: LOGIN_USER,
};
};
const loginUserSuccess = (user) => {
return {
type: LOGIN_USER_SUCCESS,
payload: user,
};
};
const loginUserFailed = (error) => {
return {
type: LOGIN_USER_FAILED,
payload: error,
};
};
export const loginUser = (dataSubmitted) => {
return (dispatch) => {
dispatch(loginUserRequest());
axios
.post("/api/users/login", dataSubmitted)
.then((response) => {
dispatch(loginUserSuccess(response.data));
})
.catch((err) => {
dispatch(loginUserFailed(err));
});
};
};
userReducer.js:
import {
LOGIN_USER,
LOGIN_USER_SUCCESS,
LOGIN_USER_FAILED,
} from "../actions/types";
const initialState = {
loading: false,
user: "",
error: "",
};
export default function (state = initialState, action) {
switch (action.type) {
case LOGIN_USER:
return { ...state, loading: true };
case LOGIN_USER_SUCCESS:
return { ...state, loading: false, user: action.payload, error: "" };
case LOGIN_USER_FAILED:
return { ...state, loading: false, user: "", error: action.payload };
default:
return state;
}
}
The above codes works great and does the job. The problem is in following code where I am dispatching the async action. After I run the code I get this.props.userData as undefined.
Login.js
import React, { Component } from "react";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { loginUser } from "../../actions/userActions";
export class Login extends Component {
state = {
email: "",
password: "",
errors: [],
};
displayErrors = (errors) =>
errors.map((error, i) => (
<div className="alert alert-danger" key={i}>
{error}
</div>
));
handleSubmit = (e) => {
e.preventDefault();
var { email, password } = this.state;
var errors = [];
if (email === "") {
errors.push("Email is required");
}
if (password === "") {
errors.push("Password is required");
}
this.setState({ errors: errors });
//Problem occurs here
this.props.dispatch(loginUser({ email, password }));
if (response.payload.success) {
sessionStorage.setItem("jwt", response.payload.token);
sessionStorage.setItem("userId", response.payload._id);
this.props.history.push("/");
} else {
errors.push("Username and/or Password is not correct");
this.setState({ errors: errors });
}
};
render() {
return (
<form className="form-signin" onSubmit={this.handleSubmit}>
<h1 className="h3 mb-3 font-weight-normal">Sign in</h1>
{this.state.errors.length > 0 && this.displayErrors(this.state.errors)}
<label for="inputEmail" className="sr-only">
Email address
</label>
<input
type="email"
id="inputEmail"
className="form-control"
placeholder="Email address"
value={this.state.email}
onChange={(e) => {
this.setState({ email: e.target.value });
}}
required
autoFocus
/>
<label for="inputPassword" className="sr-only">
Password
</label>
<input
type="password"
id="inputPassword"
className="form-control"
placeholder="Password"
value={this.state.password}
onChange={(e) => {
this.setState({ password: e.target.value });
}}
required
/>
<div className="checkbox mb-3">
<label>
<input type="checkbox" value="remember-me" /> Remember me
</label>
</div>
<button
className="btn btn-lg btn-primary btn-block"
type="submit"
onClick={this.handleSubmit}
>
Sign in
</button>
<Link to="/register">Sign Up</Link>
</form>
);
}
}
function mapStateToProps(state) {
return {
userData: state.user,
};
}
export default connect(mapStateToProps)(Login);
You need to install connected-react-router to manipulate with a history inside redux:
import { push } from 'connected-react-router';
export const loginUser = (dataSubmitted) => {
return (dispatch) => {
dispatch(loginUserRequest());
axios
.post("/api/users/login", dataSubmitted)
.then((response) => {
dispatch(loginUserSuccess(response.data));
if (response.payload.success) {
sessionStorage.setItem("jwt", response.payload.token);
sessionStorage.setItem("userId", response.payload._id);
push("/");
} else {
errors.push("Username and/or Password is not correct");
}
})
.catch((err) => {
dispatch(loginUserFailed(err));
});
};
};

When I click on button to edit my value, the state don't update the input value

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.

PUT Method in action and reducer in Redux

I have created an UPDATE_ITEM type but how to code it in here I don't have the idea since I want to update it by its ID. Also I want to dipatch this action with the reducer itemReducer.js which I have given in the code below. Can someone please help me with the code?//itemAction.js Action. The get, delete and add routes is working but this put method is giving me headache.
import axios from 'axios';
import { GET_ITEMS, ADD_ITEM, DELETE_ITEM, UPDATE_ITEM, ITEMS_LOADING } from'./types';
export const getItems = () => dispatch => {
dispatch(setItemsLoading());
axios.get('/api/items').then(res =>
dispatch({
type: GET_ITEMS,
payload: res.data
})
);
};
export const addItem = item => dispatch => {
axios.post('/api/items', item).then(res =>
dispatch({
payload: res.data
})
);
};
//UPDATE_ITEM Action Here
export const deleteItem = id => dispatch => {
axios.delete(`/api/items/${id}`).then(res =>
dispatch({
type: DELETE_ITEM,
payload: id
})
);
};
export const setItemsLoading = () => {
return {
type: ITEMS_LOADING
};
};
This is the itemReducer which I also need help
import { GET_ITEMS, DELETE_ITEM, UPDATE_ITEM, ADD_ITEM, ITEMS_LOADING
} from '../actions/types';
const initialState = {
items: [],
loading: false
};
export default function(state = initialState, action) {
switch (action.type) {
case GET_ITEMS:
return {
...state,
items: action.payload,
loading: false
};
case DELETE_ITEM:
return {
...state,
items: state.items.filter(item => item._id !== action.payload)
};
//UPDATE_ITEM Reducer here
case ADD_ITEM:
return {
...state,
items: [action.payload, ...state.items]
};
case ITEMS_LOADING:
return {
...state,
loading: true
};
default:
return state;
}
}
I guess your update looks like the add:
//UPDATE_ITEM Action
export const updateItem = item => dispatch => {
axios.put('/api/items', item).then(res =>
dispatch({
type:'UPDATE_ITEM',
payload: res.data
})
);
};
In your reducer you can use map
case UPDATE_ITEM:
return {
...state,
items: state.items.map(
item =>
item._id === action.payload.id
//return action payload (modified item) instead of
// original item when item id is updated item id
? action.payload
: item//ids not the same, return original item
)
};
import React, { Component } from 'react';
import { Container, ListGroup, ListGroupItem, Button } from 'reactstrap';
import { CSSTransition, TransitionGroup } from 'react-transition-group';
import { connect } from 'react-redux';
import { getItems, deleteItem, updateItem } from '../actions/itemActions';
import PropTypes from 'prop-types';
class ShoppingList extends Component {
componentDidMount() {
this.props.getItems();
}
onDeleteClick = id => {
this.props.deleteItem(id);
};
render() {
const { items } = this.props.item;
return (
<Container>
<ListGroup>
<TransitionGroup className="shopping-list">
{items.map(({ _id, name, body }) => (
<CSSTransition key={_id} timeout={500} classNames="fade">
<ListGroupItem>
<Button
className="remove-btn"
color="danger"
size="sm"
onClick={this.onDeleteClick.bind(this, _id)}
>
DELETE
</Button>
{/*An update button will be called here*/}
{/*<Button
className="update-btn"
color="success"
size="sm"
>
Update
</Button>*/}
<div></div>
<table className="rows">
<thead>
<tr>
<td>Name</td>
<td>Body</td>
</tr>
</thead>
<tbody>
<tr>
<td>{ name }</td>
<td>{ body }</td>
</tr>
</tbody>
</table>
</ListGroupItem>
</CSSTransition>
))}
</TransitionGroup>
</ListGroup>
</Container>
);
}
}
ShoppingList.propTypes = {
getItems: PropTypes.func.isRequired,
item: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
item: state.item
});
export default connect(
mapStateToProps,
{ getItems, deleteItem }
)(ShoppingList);

How to update state in react-redux?

I have made a submit form. I have made userReducer where user[] is array and each array element has firstname, lastname,emailid etc. When I click on submit button it shows array element [0] but when I click on clear button and again try to fill in the form and try to submit no user is added again i.e no state is updated. How to fix this problem ?
Form component (form.js) :
import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as action from '../actions/actions';
import './form.css';
class Form extends Component {
constructor(props) {
super(props);
this.setFirstName = this.setFirstName.bind(this);
this.setLastName = this.setLastName.bind(this);
this.setEmailId = this.setEmailId.bind(this);
this.setIban = this.setIban.bind(this);
this.setBankName = this.setBankName.bind(this);
this.showUser = this.showUser.bind(this);
this.reset = this.reset.bind(this);
console.log(this.props);
}
setFirstName(event) {
this.props.dispatch(action.setFirstName(event.target.value));
}
setLastName(event) {
this.props.dispatch(action.setLastName(event.target.value));
}
setEmailId(event) {
this.props.dispatch(action.setEmailId(event.target.value));
}
setIban(event) {
this.props.dispatch(action.setIban(event.target.value));
}
setBankName(event) {
this.props.dispatch(action.setBankName(event.target.value));
}
showUser(){
const jsonobj = this.props;
alert(JSON.stringify(jsonobj));
}
reset(){
this.props.dispatch(action.setFirstName(''));
this.props.dispatch(action.setLastName(''));
this.props.dispatch(action.setEmailId(''));
this.props.dispatch(action.setIban(''));
this.props.dispatch(action.setBankName(''));
}
render(){
return(
<div>
<div id="center">
<form>
<div className="form-group">
<label htmlFor="firstname">First Name:</label>
<input type="firstname" className="form-control" id="firstname" value={this.props.firstname} onChange={this.setFirstName} required/>
</div>
<div className="form-group">
<label htmlFor="lastname">Last Name:</label>
<input type="lastname" className="form-control" id="lastname" value={this.props.lastname} onChange={this.setLastName} required/>
</div>
<div className="form-group">
<label htmlFor="email">Email address:</label>
<input type="email" className="form-control" id="email" value={this.props.emailid} onChange={this.setEmailId} required/>
</div>
<div className="form-group">
<label htmlFor="bankacc">IBAN:</label>
<div id="deletebank" className="items">
<input type="bankacc" className="form-control" id="bankacc" value={this.props.iban} onChange={this.setIban} required/>
<button type="button" className="btn btn-default btn-sm">
<span className="glyphicon glyphicon-trash"></span>
</button>
</div>
</div>
<div className="form-group">
<label htmlFor="bankname">Bank Name:</label>
<input type="bankname" className="form-control" id="bankname" value={this.props.bankname} onChange={this.setBankName} required/>
</div>
<div className="form-group">
<div id="buttons" className="items">
<button type="button" class="btn btn-warning" onClick={this.reset}>Clear Input</button>
<button type="button" className="btn btn-success" onClick={this.showUser}>Submit</button>
</div>
</div>
</form>
</div>
</div>
)}
}
const mapStateToProps = store => {
return {
firstname: store.user.firstname,
lastname: store.user.lastname,
emailid: store.user.emailid,
iban: store.user.iban,
bankname: store.user.bankname
}
}
export default connect(mapStateToProps)(Form);
reducer.js:
const userReducer = (state = {
user:[{
firstname:'',
lastname:'',
emailid:'',
bankaccounts:{
iban:'',
bankname:''
}
}]
}, action) => {
switch (action.type) {
case 'SET_FIRSTNAME':{
return {
...state,
user:{...state.user, firstname: action.payload}
}
}
case 'SET_LASTNAME':{
return {
...state,
user:{...state.user, lastname: action.payload}
}
}
case 'SET_EMAILID':{
return {
...state,
user:{...state.user, emailid: action.payload}
}
}
case 'SET_IBAN':{
return {
...state,
user:{...state.user, iban: action.payload}
}
}
case 'SET_BANKNAME':{
return {
...state,
user:{...state.user, bankname: action.payload}
}
}
default: return state;
}
}
export default userReducer;
Actions.js:
export const SET_FIRSTNAME = 'SET_FIRSTNAME';
export const SET_LASTNAME = 'SET_LASTNAME';
export const SET_EMAILID = 'SET_EMAILID';
export const SET_IBAN = 'SET_IBAN';
export const SET_BANKNAME = 'SET_BANKNAME';
export function setFirstName(firstname){
return {
type:SET_FIRSTNAME,
payload:firstname
}
}
export function setLastName(lastname){
return {
type:SET_LASTNAME,
payload:lastname
}
}
export function setEmailId(emailid){
return {
type:SET_EMAILID,
payload:emailid
}
}
export function setIban(iban){
return {
type:SET_IBAN,
payload:iban
}
}
export function setBankName(bankname){
return {
type:SET_BANKNAME,
payload:bankname
}
}
store.js:
import { createStore } from 'redux';
import userReducer from './reducers/reducers';
const store = createStore(userReducer);
store.subscribe(() => {
console.log('Store changed', store.getState());
})
export default store;
Screenshot:
There's a bunch of stuff to address here, but the primary issue is that your state is expecting user to be an array. I think it would be very wise to rename this users as to not get confused:
(I'm going to remove some keys to make this easier)
const initialState = {
users: [ // note "users" not "user"
{
firstname: '',
lastname: '',
},
],
};
Your reducer switch statements don't specify WHICH user it should be updating. If you want to just start with "adding" a new user it might look something like this:
const userReducer = (state = initialState, action) => {
switch (action.type) {
case "ADD_USER": {
return {
...state,
users: [...state.users, action.payload],
};
}
default:
return state;
}
};
Here we add a new user to the users array in the form of action.payload which should contain all the keys you want on your user.
You can now have an action creator that's a bit more concise
const addUser = user => ({
type: 'ADD_USER',
payload: user,
})
And your form could be simplified a lot:
import * as actions from './actions'
class Form extends React.Component {
state = {
firstname: '',
lastname: '',
}
render() {
return (
<form onSubmit={() => {
this.props.dispatch(actions.addUser(this.state))
}}>
<input
value={this.state.firstname}
onChange={e => this.setState({ firstname: e.target.value })
/>
<input
value={this.state.lastname}
onChange={e => this.setState({ lastname: e.target.value })
/>
</form>
)
}
}
export default connect()(Form)

Categories