First Compoent
import React from "react";
import ReactDOM from "react-dom";
import PropTypes from 'prop-types'
import { withRouter } from "react-router-dom";
import { gateway as MoltinGateway } from "#moltin/sdk";
import {getList,updateList} from "./../Action/Action";
import { connect } from "react-redux";
import Icon from '#material-ui/core/Icon';
import Payment from "./../Payment/Payment";
import Tick from './done.png'
export class Item extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.pickItem = this.pickItem.bind(this);
}
UpdateList ={}
pickItem(pickedItem, id) {
//console.log(pickedItem,id)
document.getElementById(id).classList.toggle("active")
this.UpdateList = pickedItem.map(function(data,i){
if(data.id == id && i<=5 && data.pickedItem!=='Yes'){
data.pickedItem = 'Yes'
return data
}else{
data.pickedItem = 'No'
return data
}
});
}
componentWillMount() {
this.props.getList();
}
updateList(){
//console.log(this.UpdateList)
this.props.updateList(this.UpdateList)
this.props.history.push({
pathname: '/Payment',
});
}
render() {
//const { pickedItem } = this.state;
const {list} = this.props
let filtereDate;
if(list!==undefined && list.length>0){
filtereDate = list.map(function(data,i){
if(i<=5){
return(
<div key={data.id} ref={data.id} id={data.id} onClick={this.pickItem.bind(this, list, data.id )} className='item-list'>
<span className="tickMark"><img src={Tick} /></span>
<div className="logoWarapper">
<img
src="https://rukminim1.flixcart.com/image/660/792/jmdrr0w0/shirt/q/q/r/xxl-tblwtshirtful-sh4-tripr-original-imaf9ajwb3mfbhmh.jpeg?q=50"
width="100"
height="100"
alt=""
/>
</div>
<div className="itemWarapper">
<h3>{data.name}</h3>
<p>
<span>₹</span>
<span>{data.id}</span>
</p>
</div>
</div>
)
}
}.bind(this));
}
return (
<div className="ItemPage">
<header>
<h1>Online shopping</h1>
<h2>Visit | Pick | Pay</h2>
</header>
{filtereDate}
<div className="btnWrp">
<button onClick={this.updateList.bind(this)} className="button">Make Payment</button>
</div>
</div>
);
}
}
Item.propTypes = {
list: PropTypes.object,
getList: PropTypes.func,
updateList:PropTypes.func
}
function mapStateToProps(state){
const Items= state
return {
list : Items.list
}
}
const mapDispatchToProps = dispatch => ({
getList: () => dispatch(getList()),
updateList: (list) =>
dispatch(updateList(list))
})
export default withRouter(connect(
mapStateToProps,
mapDispatchToProps
)(Item));
Sages file
import { put, takeLatest, all, call,select } from "redux-saga/effects";
function* fetchNews() {
const json = yield fetch(
"http://petstore.swagger.io/v2/pet/findByStatus?status=available"
).then(response => response.json());
yield put({ type: "GET_LIST_SUCCESS", json: json });
}
function * updateNewList(data){
///console.log(data.payload)
yield put({ type: "GET_LIST_SUCCESS", list: data.payload });
}
function * fetchupateList(){
const signals = yield select(store => store)
console.log(signals)
}
function* actionWatcher() {
yield takeLatest("GET_LIST_REQUEST", fetchNews);
yield takeLatest("GET_UPDATED_LIST_REQUEST", fetchupateList);
yield takeLatest("UPDATE_LIST_REQUEST", updateNewList);
}
export default function* rootSaga() {
yield all([actionWatcher()]);
}
**Second Component **
import React from "react";
import ReactDOM from "react-dom";
import PropTypes from 'prop-types'
import { withRouter } from "react-router-dom";
import { connect } from "react-redux";
import { makeStyles } from "#material-ui/core/styles";
import TextField from "#material-ui/core/TextField";
import Button from "#material-ui/core/Button";
import {getList,updateList,getUpdatedList} from "./../Action/Action";
export class Payment extends React.Component {
constructor(props) {
super(props);
this.state = {
pickedItem: [1, 2]
};
}
componentWillMount() {
this.props.getUpdatedList();
}
render() {
console.log(this.props)
const { pickedItem } = this.state;
//console.log(pickedItem);
return (
<div className="PaymentPage">
<div className="pageWrapper">
<form noValidate autoComplete="off">
<h1>Payment Details</h1>
<TextField
id="outlined-name"
label="Card Type"
margin="normal"
variant="outlined"
/>
<TextField
id="outlined-name"
label="Card Name"
margin="normal"
variant="outlined"
/>
<TextField
id="outlined-name"
label="Card Number"
margin="normal"
variant="outlined"
/>
<div className="clm-2-inp">
<TextField
id="outlined-name"
label="Expiry Date (MM/YYYY)"
margin="normal"
variant="outlined"
/>
<TextField
id="outlined-name"
label="CVV"
margin="normal"
variant="outlined"
/>
</div>
</form>
<div className="itemsection">
<h2>Summery</h2>
<div className="item-list">
<div className="logoWarapper">
<img
src="https://rukminim1.flixcart.com/image/660/792/jmdrr0w0/shirt/q/q/r/xxl-tblwtshirtful-sh4-tripr-original-imaf9ajwb3mfbhmh.jpeg?q=50"
width="100"
height="100"
alt=""
/>
</div>
<div className="itemWarapper">
<h3>Item Name</h3>
<p>
<span>₹</span>
<span>3000</span>
</p>
</div>
</div>
<Button variant="contained" color="primary">
Submit Purchase
</Button>
</div>
</div>
</div>
);
}
}
Payment.propTypes = {
list: PropTypes.object,
getList: PropTypes.func,
updateList:PropTypes.func,
getUpdatedList:PropTypes.func
}
function mapStateToProps(state,ownProps){
console.log(state,ownProps)
const Items= state
return {
list : Items.list
}
}
const mapDispatchToProps = {
getList,
updateList,
getUpdatedList
};
export default withRouter(connect(
mapStateToProps,
mapDispatchToProps
)(Payment));
Reducer
const initialState = {
list: {}
}
const Reducer = (state = initialState, action) => {
switch (action.type) {
case "GET_LIST_SUCCESS":
return {
...state,
list: action.json,
}
case "GET_LIST_SUCCESS":
return {
...state,
list: action.list,
}
default:
return state;
}
};
export default Reducer;
Once i click the "Make payment" button in the first component, i will updated the list with some modification those modified changes i want to get in the second component.
I unable to get first redux store value in the second component.
Help me to ix this issue please.
Related
how to add an action when the add button is clicked then the item will display the product?
This code does not display the product when clicked
I've been fiddling with it but it's still an error, who knows, someone here can help me and fix the code and explain it
cartAction.js
import {ADD_TO_CART} from './ActionType';
export const addToCart = (items) =>{
return{
type: ADD_TO_CART,
items //I'm confused about what to do in the action payload here
}
}
cartReducer.js
import ImgT from './images/image-product-1-thumbnail.jpg';
import {ADD_TO_CART} from './ActionType';
const initState = {
items: [
{id:1,title:'title',
brand:cc',
images:ImgT,
desc:'helo world',
price:125.00}
],addItems:[],
total:0
}
const cartReducer= (state = initState,action)=>{
if(action.type === ADD_TO_CART){
return {...state, addItems:state.addItems} //I'm confused about what to do in the action payload here
}
return state
}
export default cartReducer;
cart.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
class Cart extends Component {
render() {
return (
<div>
{this.props.Items.map(item =>{
<div key={item.id}>
{/* <img src={item.images} /> */}
<p>{item.title}</p>
<h4>brand: {item.brand}</h4>
<p>{item.price}</p>
</div>
})
}
</div>
);
}
}
const mapToProps = (state ) =>{
return{
Items:state.addItems}
}
export default connect(mapToProps)(Cart);
Home.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Cart from './Cart';
import {addToCart} from './cartAction';
class Home extends Component{
handleClick = () =>{
this.props.addToCart()
}
render(){
let item = this.props.items.map(item =>{
return(
<div className='card' key={item}>
<img style={{width:'10rem'}} src={item.images}/>
<p className='card-title '>{item.title}</p>
<h2 className='card-title fs-4'>{item.brand}</h2>
<button onClick={() => this.handleClick()} className='btn btn-primary'>Add to cart</button>
<h3 className='fs-5'>{item.price}</h3>
</div>
)
})
return(
<div className="container">
<h3>Home</h3>
{item}
<Cart/>
</div>
)
}
}
const mapToProps = (state) => {
return{
items:state.items,
}
}
const mapDispatchToProps = (dispatch) => {
return{
addToCart: () => dispatch(addToCart())}
}
export default connect(mapToProps,mapDispatchToProps)(Home);
I have been trying to create a React app using redux and redux-saga, but I haven't been able to make it work, and I alway get the value of undefined as the result.
This is my component catalogs/index.js:
import React, {Component} from 'react';
import {connect} from 'react-redux';
import CatalogHeader from './CatalogHeader';
import CircularProgress from '#material-ui/core/CircularProgress';
import {getCatalogs} from 'actions/Catalogs';
import IntlMessages from 'util/IntlMessages';
import CustomScrollbars from 'util/CustomScrollbars';
class Catalogs extends Component {
constructor() {
super();
this.state = {
anchorEl: null
}
}
updateSearch = (evt) => {
this.props.updateSearch(evt.target.value);
this.onSearchTodo(evt.target.value)
};
render() {
const {catalogsList} = this.props;
return (
<div className="app-wrapper">
<div className="animated slideInUpTiny animation-duration-3">
<div className="app-module">
<div className="module-box">
<div className="module-box-header">
<CatalogHeader catalogsList={catalogsList}
placeholder="Buscar" user={this.props.user}
onChange={this.updateSearch.bind(this)}
value={this.props.searchTodo}/>
</div>
</div>
</div>
</div>
</div>
)
}
}
const mapStateToProps = ({catalogs, settings}) => {
const {width} = settings;
const {catalogsList} = catalogs;
return {
width,
catalogsList,
}
};
export default connect(mapStateToProps, {
getCatalogs,
})(Catalogs);
This is the other component catalogs/CatalogHeader.js
import React from 'react';
import IconButton from '#material-ui/core/IconButton';
import Button from '#material-ui/core/Button';
import {Dropdown, DropdownMenu, DropdownToggle, Popover} from 'reactstrap';
import SearchBox from 'components/SearchBox';
import Input from '#material-ui/core/Input';
import InputLabel from '#material-ui/core/InputLabel';
import MenuItem from '#material-ui/core/MenuItem';
import FormControl from '#material-ui/core/FormControl';
import FormHelperText from '#material-ui/core/FormHelperText';
import Select from '#material-ui/core/Select';
class CatalogHeader extends React.Component {
handleChange = name => event => {
this.setState({[name]: event.target.value});
};
onSearchBoxSelect = () => {
this.setState({
searchBox: !this.state.searchBox
})
};
constructor() {
super();
this.state = {
anchorEl: undefined,
searchBox: false,
popoverOpen: false
};
this.toggle = this.toggle.bind(this);
}
toggle() {
this.setState({
popoverOpen: !this.state.popoverOpen
});
}
printCatalogs(catalogs) {
console.log(catalogs);
}
render() {
const {catalogs} = this.props;
return (
<div className="module-box-header-inner catalogs-header">
<div className="col-5 catalogs-header">
<FormControl className="w-100 mb-2">
{this.printCatalogs(this.props.catalogs)}
<InputLabel htmlFor="age-simple">Seleccione Catálogo</InputLabel>
<Select
value={this.state.age}
onChange={this.handleChange('age')}
input={<Input id="ageSimple1"/>}>
{catalogs.map(catalog =>
<MenuItem key={catalog}>
{catalog}
</MenuItem>,
)}
<MenuItem value="">
<em>None</em>
</MenuItem>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
</FormControl>
</div>
<div className="module-box-header-right col-7 catalogs-header">
<div className="search-bar right-side-icon bg-transparent d-none d-sm-block col-6">
<div className="form-group">
<input className="form-control border-0" type="search" placeholder="Buscar"/>
<button className="search-icon"><i className="zmdi zmdi-search zmdi-hc-lg"/></button>
</div>
</div>
<div className="col-6">
<div className="catalogs-header catalogs-header-buttons">
<Button variant="contained" className="jr-btn bg-white">
<i className="zmdi zmdi-filter-list zmdi-hc-fw"/>
<span>Filtrar</span>
</Button>
<Button variant="contained" className="jr-btn bg-white">
<i className="zmdi zmdi-sort zmdi-hc-fw"/>
<span>Ordenar</span>
</Button>
</div>
</div>
</div>
</div>
)
}
}
export default CatalogHeader;
This is my actions file:
import {
GET_CATALOGS,
GET_CATALOGS_SUCCESS,
SHOW_MESSAGE
} from 'constants/ActionTypes';
export const getCatalogs = (group) => {
return {
type: GET_CATALOGS,
payload: group
};
};
export const getCatalogsSuccess = (catalogs) => {
return {
type: GET_CATALOGS_SUCCESS,
payload: catalogs
}
};
export const showCatalogsMessage = (message) => {
return {
type: SHOW_MESSAGE,
payload: message
};
};
This is my reducers file:
import {
GET_CATALOGS,
SHOW_MESSAGE,
HIDE_MESSAGE,
GET_CATALOGS_SUCCESS
} from 'constants/ActionTypes';
const INIT_STATE = {
loader: false,
alertMessage: '',
showMessage: false,
catalogsList: null
};
export default (state=INIT_STATE, action) => {
switch (action.type) {
case GET_CATALOGS_SUCCESS: {
return {
...state,
loader: false,
catalogsList: action.payload
}
}
case SHOW_MESSAGE: {
return {
...state,
alertMessage: action.payload,
showMessage: true,
loader: false
}
}
case HIDE_MESSAGE: {
return {
...state,
alertMessage: '',
showMessage: false,
loader: false
}
}
default:
return state;
}
}
This is my sagas file:
import {all, call, fork, put, takeEvery} from "redux-saga/effects";
import {catalogs} from 'backend/Catalogs';
import {GET_CATALOGS} from "constants/ActionTypes";
import {getCatalogs, getCatalogsSuccess, showCatalogsMessage} from 'actions/Catalogs';
const getCatalogsRequest = async (group) => {
await catalogs.getCatalogs(group)
.then(catalogsList => catalogsList)
.catch(error => error);
}
function* getCatalogsListFromRequest({payload}) {
const {group} = payload;
try {
const catalogsList = yield call(getCatalogsRequest, group);
if (catalogsList.message) {
yield put(showCatalogsMessage(catalogsList.Message));
} else {
yield put(getCatalogsSuccess(catalogsList.catalogsList))
}
} catch (error) {
yield put(showCatalogsMessage(error));
}
}
export function* getCatalogsList() {
yield takeEvery(GET_CATALOGS, getCatalogsListFromRequest);
}
export default function* rootSaga() {
yield all([
fork(getCatalogsList)
]);
}
And this is the file that performs the ajax request through Axios (I know for sure that this code is never reached, and a request to the backend server is never performed):
import axios from 'axios';
import {backendServer} from './constants';
const getCatalogsEndpoint = backendServer + 'api/util/catalogs/';
const getCatalogs = (group) => {
console.log('here inside getCatalogs in backend/Catalogs');
return axios.get(getCatalogsEndpoint+(group=null)?null:'?group='+group)
.then(Response => {
return {catalogsList: Response.data}
})
.catch(Error => Error);
};
export const catalogs = {
getCatalogs: getCatalogs
}
The problem arises in CatalogsHeader.js, because this.props.catalogsList always has the value of undefined:
I see problem in CatalogHeader. I think, you are referring a wrong pop.
In index.js you are passing the prop as catalogsList={catalogsList} to CatalogHeader.
But in CatalogHeader you are accessing prop as const {catalogs} = this.props; i.e. catalogs which is undefined. Please try changing this into catalogsList.
I crafted a reactjs crud app with help of a tutorial and it works great now. Now i am trying to merge two form together so that same form should be used for both add and update operation.
This is my allpost.js file
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Post from '../components/Post';
import EditComponent from '../components/editComponent';
class AllPost extends Component {
render() {
return (
<div>
<h1>All Posts</h1>
{this.props.posts.map((post) => (
<div key={post.id}>
{post.editing ? <EditComponent post={post} key={post.id} /> :
<Post key={post.id} post={post} />}
</div>
))}
</div>
);
}
}
const mapStateToProps = (state) => {
return {
posts: state
}
}
export default connect(mapStateToProps)(AllPost);
and this is my postForm.js file:
import React, { Component } from 'react';
import { connect } from 'react-redux'
class PostForm extends Component {
handleSubmit = (e) => {
e.preventDefault();
const title = this.getTitle.value;
const message = this.getMessage.value;
const data = {
id: new Date(),
title,
message,
editing: false
}
console.log(data)
this.props.dispatch({
type: 'ADD_POST',
data,
});
this.getTitle.value = '';
this.getMessage.value = '';
}
render() {
return (
<div>
<h1>Create Post</h1>
<form onSubmit={this.handleSubmit}>
<input required type="text" ref={(input)=>this.getTitle = input}
placeholder="Enter Post Title"/>
<br /><br />
<textarea required rows="5" ref={(input)=>this.getMessage = input} cols="28"
placeholder="Enter Post" />
<br /><br />
<button>Post</button>
</form>
</div>
);
}
}
export default connect()(PostForm);
and this is my editComponent.js file
import React, { Component } from 'react';
import { connect } from 'react-redux';
class EditComponent extends Component {
handleEdit = (e) => {
e.preventDefault();
const newTitle = this.getTitle.value;
const newMessage = this.getMessage.value;
const data = {
newTitle,
newMessage
}
this.props.dispatch({ type: 'UPDATE', id: this.props.post.id, data: data })
}
render() {
return (
<div>
<form onSubmit={this.handleEdit}>
<input required type="text" ref={(input) => this.getTitle = input}
defaultValue={this.props.post.title} placeholder="Enter Post Title" /><br /><br />
<textarea required rows="5" ref={(input) => this.getMessage = input}
defaultValue={this.props.post.message} cols="28" placeholder="Enter Post" /><br /><br />
<button>Update</button>
</form>
</div>
);
}
}
export default connect()(EditComponent);
and this is my post.js file:
import React, { Component } from 'react';
import { connect } from 'react-redux'
class Post extends Component {
render() {
return (
<div>
<h2>{this.props.post.title}</h2>
<p>{this.props.post.message}</p>
<button onClick={() => this.props.dispatch({type: 'EDIT_POST', id: this.props.post.id})}>EDIT
</button>
<button onClick={ () => this.props.dispatch({type: 'DELETE_POST', id: this.props.post.id}) }>DELETE
</button>
</div>
);
}
}
export default connect()(Post);
and this is my postReducer.js file:
const postReducer = (state = [], action) => {
switch(action.type) {
case 'ADD_POST':
return state.concat([action.data]);
case 'DELETE_POST':
return state.filter((post)=>post.id !== action.id);
case 'EDIT_POST':
return state.map((post)=>post.id === action.id ? {...post,editing:!post.editing}:post)
case 'UPDATE':
return state.map((post)=>{
if(post.id === action.id) {
return {
...post,
title:action.data.newTitle,
message:action.data.newMessage,
editing: !post.editing
}
} else return post;
})
default:
return state;
}
}
export default postReducer;
Can anyone please help me to achieve this? I tried a lot to use same form form for both add and update and i failed to achieve this.
I think it's better you create separate component for rendering form data(FormComponent) and separate components for edit(EditComponent) and add(AddComponent).
This way there will not be clutter in one component and no if/else conditions for different modes like edit or add, or in future copy mode.
This approach will add flexibility and enhances compositional pattern of react.
1) AddComponent
import React, { Component } from 'react';
import { connect } from 'react-redux'
class AddComponent extends Component {
handleSubmit = (title, message) => {
const data = {
id: new Date(),
title,
message,
editing: false
}
this.props.dispatch({
type: 'ADD_POST',
data,
});
}
render() {
return (
<div>
<h1>Create Post</h1>
<FormComponent
buttonLabel='Post'
handleSubmit={this.handleSubmit}
/>
</div>
);
}
}
export default connect()(AddComponent);
2) EditComponent
import React, { Component } from 'react';
import { connect } from 'react-redux';
class EditComponent extends Component {
handleSubmit = (newTitle, newMessage) => {
const data = {
newTitle,
newMessage
}
this.props.dispatch({ type: 'UPDATE', id: this.props.post.id, data: data })
}
render() {
return (
<div>
<FormComponent
buttonLabel='Update'
handleSubmit={this.handleSubmit}
/>
</div>
);
}
}
export default connect()(EditComponent);
3) FormComponent
import React, { Component } from 'react';
class FormComponent extends Component {
handleSubmit = (e) => {
e.preventDefault();
const title = this.getTitle.value;
const message = this.getMessage.value;
this.props.handleSubmit(title, message);
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<input required type="text" ref={(input) => this.getTitle = input}
defaultValue={this.props.post.title} placeholder="Enter Post Title" /><br /><br />
<textarea required rows="5" ref={(input) => this.getMessage = input}
defaultValue={this.props.post.message} cols="28" placeholder="Enter Post" /><br /><br />
<button>{this.props.buttonLabel}</button>
</form>
);
}
}
export default FormComponent;
Hope that helps!!!
You can create your own Form component with a prop of editMode to control whether it's Create or Update.
import React, {Component} from 'react';
import PropTypes from 'prop-types';
class Form extends Component {
handleSubmit = e => {
e.preventDefault();
const {editMode, post} = this.props;
const title = this.titleRef.value;
const body = this.bodyRef.value;
if (editMode){
const data = {
title,
body
}
this.props.dispatch({type: 'UPDATE', id: post.id, data})
}
else {
const data = {
id: new Date(),
title,
message,
editing: false
}
this.props.dispatch({type: 'ADD_POST', data});
}
}
render() {
const {editMode, post} = this.props;
const pageTitle = editMode ? 'Edit Post' : 'Create Post';
const buttonTitle = editMode ? 'Update' : 'Post';
return (
<div>
<h1>{pageTitle}</h1>
<form onSubmit={this.handleSubmit}>
<input
required
type="text"
ref={input => this.titleRef = input}
placeholder="Enter Post Title"
defaultValue={post.title}
/>
<textarea
required
rows="5"
ref={input => this.bodyRef = input}
cols="28"
placeholder="Enter Post"
defaultValue={post.body}
/>
<button>{buttonTitle}</button>
</form>
</div>
);
}
}
Form.propTypes = {
editMode: PropTypes.bool,
post: PropTypes.object
}
Form.defaultProps = {
editMode: false, // false: Create mode, true: Edit mode
post: {
title: "",
body: ""
} // Pass defined Post object in create mode in order not to get undefined objects in 'defaultValue's of inputs.
}
export default Form;
It would be on create mode by default but if you wanna update the post you should pass editMode={true} to your form component.
I not 100% sure if I am doing this right as per the redux design.
import React from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import 'materialize-css/sass/materialize.scss';
import NavigationContainer from './NavigationContainer';
import AddStorageModal from './AddStorageModal.js'
import {loadAddStorageModal, createNewStorage} from '../actions/StorageActions.js'
import '../styles/main.scss';
class App extends React.Component {
render() {
return (
<div>
<NavigationContainer />
<AddStorageModal {...this.props} />
</div>
)
}
}
function mapStateToProps(state) {
return {
storages: state.storages
};
}
function matchDispatchToProps(dispatch){
return bindActionCreators({loadAddStorageModal: loadAddStorageModal, createNewStorage: createNewStorage}, dispatch);
}
export default connect(mapStateToProps, matchDispatchToProps)(App);
StorageActions
export function fetchStorage() {
return function(dispatch) {
var payload = [
{
id: 1,
name: "Fridge2"
},
{
id: 2,
name: "Closet2"
},
{
id: 3,
name: "Car2"
}
];
dispatch({type: "Fetch_Storage", payload: payload});
}
}
export function loadAddStorageModal(load) {
return function(dispatch) {
dispatch({type: "Load_Add_Storage_Modal", payload: load});
}
}
export function createNewStorage(storage) {
return function(dispatch) {
dispatch({type: "New_Storage_Created", payload: storage});
}
}
Reducer
export default function reducer(state = {
fetchedStorages: [],
openAddStorageModal: false
}, action) {
switch (action.type) {
case "Fetch_Storage": {
return {
fetchedStorages: action.payload
}
}
case "Load_Add_Storage_Modal": {
return {
openAddStorageModal: action.payload,
fetchedStorages: state.fetchedStorages
}
}
case "New_Storage_Created": {
return {
openAddStorageModal: false,
}
}
}
return state;
}
AddStorageModal
import React from 'react';
import 'materialize-css/sass/materialize.scss';
import 'materialize-css/js/materialize.js';
import 'font-awesome/scss/font-awesome.scss';
import '../styles/main.scss';
export default class AddStorageModal extends React.Component {
constructor() {
super();
this.state = {storageName: ""}
}
handleChange(event) {
this.setState({storageName: event.target.value});
}
render() {
if (this.props.storages.openAddStorageModal) {
$('#add-new-storage-modal').openModal({ dismissible: false });
}
return (
<div id="add-new-storage-modal" className="modal" >
<div className="modal-content">
<h6>Enter your new Storage (Freezer, Pantry, etc.) </h6>
<div className="row">
<form>
<div className="input-field col s12 m12 l12 ">
<input id="storage_name" type="text" className="validate" value={this.state.storageName} onChange={this.handleChange} />
<label htmlFor="storage_name">Storage Name</label>
</div>
<br />
<h4 className="center">OR</h4>
<h6>Enter in the sharing key you were given.</h6>
<div className="input-field col s12 m12 l12 ">
<input id="sharing_key" type="text" className="validate" />
<label htmlFor="sharing_key">Sharking Key</label>
</div>
</form>
</div>
</div>
<div className="modal-footer">
Add
<a href="#!" className="modal-action modal-close waves-effect waves-green btn-flat" onClick={() => this.props.loadAddStorageModal(false) }>Cancel</a>
</div>
</div>
)
}
}
I get
Uncaught TypeError: Cannot read property 'setState' of undefined
So I am not sure if this just means I am doing redux wrong or if I just made some general error.
You can't pass a generic function reference, you need to keep the reference to this. You have 2 options:
bind this to the function, like #nuway said in his answer.
Use an arrow function, which also keeps the this reference: onChange={ (event) => this.handleChange(event) }
you need to bind to this for the handleChange handler, otherwise this inside handleChange funtion won't be the react component but rather the input element itself.
onChange={this.handleChange.bind(this)}
i don't know what im doing wrong.
I thought i got the point how to implement with redux.
My Problem is that the state of the Component is not propagating to the Component after the state changed. I know we have to create new state object and i'm sure im doing it right. But there is no changes.
And the other question is, why the state is in the object "resetLink" see image. Why is it not stored in the state Object?
Please help me to get it work.
Action for the redux:
export const sendResetLink = (email) => {
return {
type: RESET_LINK,
email
}
}
class App extends React.Component {
render() {
return <VisibleResetLink/>
}
}
This is where the magic with props and dispatch function happens:
import {connect} from 'react-redux';
import SendResetLink from '../components/SendResetLink.jsx';
import {sendResetLink} from '../actions/index'
import _ from 'lodash';
const mapDispatchToProps = (dispatch) => {
return {
onClick: (email) => {
dispatch(sendResetLink(email))
}
}
}
const mapStateToProps = (state) => {
console.log("actual state", state);
return _.assign({} , {state: state.resetLink});
}
const VisibleResetLink = connect(
mapStateToProps,
mapDispatchToProps
)(SendResetLink)
export default VisibleResetLink
This is the Component which load props but never rerender them when they change.
import React, { PropTypes } from 'react';
import ReactDOM from 'react-dom';
class SendResetLink extends React.Component {
constructor(props, email, result) {
super(props);
console.log('props',props);
this.onClick = props.onClick;
this.result = props.state.result;
this.input = props.state.email;
}
renderResult (result){
console.log("renderResult",result);
if(result)
return <div className="card-panel deep-orange lighten-4">Invalid username and password.</div>
}
render() {
return <div>
{this.renderResult(this.result)}
{this.result}
<form className="col s12" action="/login" method="post" onSubmit={e => {
e.preventDefault()
if (!this.input.value.trim()) {
return
}
this.onClick(this.input.value);
this.input.value = ''
} }>
<div className="row">
<div className="input-field col s12">
<i className="material-icons prefix">email</i>
<input id="email" type="email" name="email" className="validate" ref = {(node) => this.input = node } />
<label for="email">Email</label>
</div>
</div>
<div className="divider"></div>
<div className="row">
<div className="col m12">
<p className="right-align">
<button className="btn btn-large waves-effect waves-light" type="submit" name="action">Send reset key</button>
</p>
</div>
</div>
</form></div>
}
}
SendResetLink.propTypes = {
onClick: PropTypes.func.isRequired,
state: PropTypes.object.isRequired
}
export default SendResetLink
And this is the other relevant code snippet, where the reducer is implemented.
import _ from 'lodash';
const resetLink = (state = {email:"test#test.de", result:true}, action) => {
console.log('state', state)
switch (action.type) {
case 'RESET_LINK':
return _.assign({},state,{email: action.email, result: false});
default:
return state
}
}
export default resetLink;
import { combineReducers } from 'redux'
import resetLink from './ResetLinkReducer.jsx'
const resetApp = combineReducers({
resetLink
})
export default resetApp
import App from './components/app.jsx';
import {Provider} from 'react-redux';
import { createStore } from 'redux';
import { render } from 'react-dom';
import React from 'react';
import resetApp from './reducers/index.js';
let store = createStore(resetApp,{});
console.log(store)
render(<Provider store={store}>
<App />
</Provider>, document.getElementById('sendResetLinkComponent'));
console logs. After click the renderResult should change to false. But it stays on true
You set this.result in your SendResetLink component only once in the constructor, which is only executed when the component is instantiated. The constructor will not be executed every time the application's state changes:
this.result = props.state.result;
Instead of assigning this part of the state to an instance attribute of your react component, simply use the props attribute directly in your render() function:
{this.renderResult(this.props.state.result)}