how to map values in material ui auto-complete - reactjs

I have got data from firebase in "arr" but I dont know how can I use material-ui Auto-Complete? how Can I map "arr" data in Auto-Complete field?
import React, { Component } from 'react'
import { Link, browserHistory } from 'react-router';
import { connect } from 'react-redux'
import RaisedButton from 'material-ui/RaisedButton';
import { DBfirebase } from '../../Database/DBfirebase'
import { signUp } from '../../Store/Actions/Auth'
import TextField from 'material-ui/TextField';
import AppBar from 'material-ui/AppBar';
import SelectField from 'material-ui/SelectField';
import MenuItem from 'material-ui/MenuItem';
import AutoComplete from 'material-ui/AutoComplete';
import * as firebase from 'firebase';
import { Search } from '../../Store/Actions/Auth'
class AddProduct extends Component {
constructor() {
super();
this.state = {
arr: [],
productName: '',
description: '',
company:''
}
this.submit = this.submit.bind(this);
this.inputHandler = this.inputHandler.bind(this);
this.onSearch = this.onSearch.bind(this);
this.handleUpdateInput = this.handleUpdateInput.bind(this);
}
inputHandler(e) {
this.setState({
[e.target.name]: e.target.value
})
}
submit(e) {
e.preventDefault();
let multipath = {};
let productDetails = {
productName: this.state.productName,
description: this.state.description,
company: this.state.company,
}
console.log(productDetails)
DBfirebase.refAddProduct.push(productDetails);
browserHistory.push('/home/view-purchases')
}
//working code
onSearch(e) {
let _self = this;
// e.preventDefault()
let ref = firebase.database().ref().child('/AddedProducts/');
_self.arr = [];
// console.log(this.refs.selectedCity.value)
// ref.orderByChild('city').equalTo(this.refs.selectedCity.value).once('value', function (snapshot) {
ref.once('value', function (snapshot) {
snapshot.forEach(childSnapshot => {
_self.arr.push(childSnapshot.val())
console.log("arr", _self.arr)
})
_self.props.serachProducts(_self.arr)
_self.setState({
arr: _self.props.storeReducer.products
})
});
}
componentWillMount(){
this.onSearch();
}
handleUpdateInput = (value) => {
this.setState({
arr: [
value,
value + value,
value + value + value,
],
});
};
render() {
return (
<div ><center>
<AddProductForm signUpState={this.state} _inputHandler={this.inputHandler} _submit={this.submit} />
</center>
</div>
);
}
}
// AddProduct.contextTypes = {
// router: React.PropTypes.object.isRequired
// }
const dataSourceConfig = {
text: 'textKey',
value: 'valueKey',
};
class AddProductForm extends React.Component {
render() {
console.log("this.props.signUpState.arr",this.props.signUpState.arr)
const datasource = this.props.signUpState.arr;
return (
<div >
<h1>Add New Product</h1>
<form onSubmit={this.props._submit} >
<AutoComplete
hintText="Product Name"
filter={AutoComplete.noFilter}
openOnFocus={true}
// dataSource={this.props.signUpState.arr}
dataSource={datasource}
onUpdateInput={this.props.signUpState.handleUpdateInput}
dataSourceConfig={dataSourceConfig}
/>
<TextField
hintText="Product Name"
name="productName"
value={this.props.signUpState.productName}
floatingLabelText="Product Name"
onChange={this.props._inputHandler}
/><br /><br />
<TextField
type="text"
hintText="description"
name="description"
value={this.props.signUpState.description}
floatingLabelText="description"
onChange={this.props._inputHandler}
/><br /><br />
<TextField
type="text"
hintText="company"
name="company"
value={this.props.signUpState.company}
floatingLabelText="company"
onChange={this.props._inputHandler}
/><br />
<br />
<RaisedButton type="submit" label="Add Product" primary={false} secondary={true} /> <br /><br />
</form>
</div>
)
}
}
AddProductForm.PropTypes = {
_inputHandler: React.PropTypes.func.isRequired,
_submit: React.PropTypes.func.isRequired
}
const mapStateToProps = (state) => {
console.log(state.ProductReducer)
return {
storeReducer: state.ProductReducer
}
}
const mapDispatchToProps = (dispatch) => {
return {
serachProducts: (data) => {
console.log(data)
dispatch(Search(data))
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(AddProduct);
I have got data from firebase in "arr" but I dont know how can I use material-ui Auto-Complete? how Can I map "arr" data in Auto-Complete field?

Actually data from firebase come in object... So first get the keys of object using Object.keys(objectname) then store all keys in Array ... After getting the keys of object then start mapping on Arrays of key and in map call ur object with keys like obj[keys] ..

import React, { Component } from 'react'
import { Link, browserHistory } from 'react-router';
import { connect } from 'react-redux'
import RaisedButton from 'material-ui/RaisedButton';
import { DBfirebase } from '../../Database/DBfirebase'
import { signUp } from '../../Store/Actions/Auth'
import TextField from 'material-ui/TextField';
import AppBar from 'material-ui/AppBar';
import SelectField from 'material-ui/SelectField';
import MenuItem from 'material-ui/MenuItem';
import AutoComplete from 'material-ui/AutoComplete';
import * as firebase from 'firebase';
import { Search } from '../../Store/Actions/Auth'
class AddProduct extends Component {
constructor() {
super();
this.state = {
arr: [],
productName: '',
description: '',
company:''
}
this.submit = this.submit.bind(this);
this.inputHandler = this.inputHandler.bind(this);
this.onSearch = this.onSearch.bind(this);
this.handleUpdateInput = this.handleUpdateInput.bind(this);
}
inputHandler(e) {
this.setState({
[e.target.name]: e.target.value
})
}
submit(e) {
e.preventDefault();
let multipath = {};
let productDetails = {
productName: this.state.productName,
description: this.state.description,
company: this.state.company,
}
console.log(productDetails)
DBfirebase.refAddProduct.push(productDetails);
browserHistory.push('/home/view-purchases')
}
//working code
onSearch(e) {
let _self = this;
// e.preventDefault()
let ref = firebase.database().ref().child('/AddedProducts/');
_self.arr = [];
// console.log(this.refs.selectedCity.value)
// ref.orderByChild('city').equalTo(this.refs.selectedCity.value).once('value', function (snapshot) {
ref.once('value', function (snapshot) {
snapshot.forEach(childSnapshot => {
_self.arr.push(childSnapshot.val())
console.log("arr", _self.arr)
})
_self.props.serachProducts(_self.arr)
_self.setState({
arr: _self.props.storeReducer.products
})
});
}
componentWillMount(){
this.onSearch();
}
render() {
return (
<div ><center>
<AddProductForm signUpState={this.state} _inputHandler={this.inputHandler} _submit={this.submit} />
</center>
</div>
);
}
}
// AddProduct.contextTypes = {
// router: React.PropTypes.object.isRequired
// }
class AddProductForm extends React.Component {
render() {
console.log("this.props.signUpState.arr",this.props.signUpState.arr)
{ this.props.signUpState.arr.map((v, i) => {
return (
datasource.push(v.productName)
)
})}
return (
<div >
<h1>Add New Product</h1>
<form onSubmit={this.props._submit} >
<AutoComplete
hintText="Product Name"
filter={AutoComplete.noFilter}
openOnFocus={true}
// dataSource={this.props.signUpState.arr}
dataSource={datasource}
// onUpdateInput={this.props.signUpState.handleUpdateInput}
// dataSourceConfig={dataSourceConfig}
/>
<TextField
hintText="Product Name"
name="productName"
value={this.props.signUpState.productName}
floatingLabelText="Product Name"
onChange={this.props._inputHandler}
/><br /><br />
<TextField
type="text"
hintText="description"
name="description"
value={this.props.signUpState.description}
floatingLabelText="description"
onChange={this.props._inputHandler}
/><br /><br />
<TextField
type="text"
hintText="company"
name="company"
value={this.props.signUpState.company}
floatingLabelText="company"
onChange={this.props._inputHandler}
/><br />
<br />
<RaisedButton type="submit" label="Add Product" primary={false} secondary={true} /> <br /><br />
</form>
</div>
)
}
}
AddProductForm.PropTypes = {
_inputHandler: React.PropTypes.func.isRequired,
_submit: React.PropTypes.func.isRequired
}
const mapStateToProps = (state) => {
console.log(state.ProductReducer)
return {
storeReducer: state.ProductReducer
}
}
const mapDispatchToProps = (dispatch) => {
return {
serachProducts: (data) => {
console.log(data)
dispatch(Search(data))
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(AddProduct);

Related

Unable to access First component state in second component using Redux,Sagas

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.

Reactjs: Using same form for add and update

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.

not able to share state between two components in react

I have two components-AskQuestion and SingleQuestion
I want to pass the data from AskQuestion to SingleQuestion. How to make this.state.form content available in SingleQuestion component.
AskQuestion.jsx
import React, { Component } from 'react';
import EditableTagGroup from '../EditableTagGroupComponent/EditableTagGroup';
import { createHashHistory } from 'history'
const history = createHashHistory();
class AskQuestion extends Component {
constructor(props) {
super(props)
this.state = {
form: {
Title: '',
Content: '',
Tags: sessionStorage.getItem("TG"),
}
};
this.onChange = this.onChange.bind(this);
this.changeHandler = this.changeHandler.bind(this);
this.submitHandler = this.submitHandler.bind(this);
}
changeHandler(e) {
e.persist();
let store = this.state;
store.form[e.target.name] = e.target.value;
this.setState(store);
}
submitHandler(e) {
e.preventDefault();
fetch('cons/ques/create', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
"Request": {
"RequestInfo": {
"userId": "2"
},
"RequestPayload": {
"Questions": [
{
"questionId": 0,
"questionTitle": this.state.form.Title,
"isAnswered": false,
"questionContent": this.state.form.Content,
"tags": [{
"tagId": 1,
"tagName": "Java",
"tagUsage": 1
}]
}
]
}
}
}
)
}).then(res => {
console.log(res);
this.redirect();
return res;
}).catch(err => err);
}
redirect = () => {
this.props.history.push('/SingleQuestion');
}
onChange(e) {
this.setState({ [e.target.name]: e.target.value });
}
render() {
const { form } = this.state;
return (
<div className="container">
<h2>ASK A QUESTION</h2>
<form onSubmit={this.submitHandler}>
<div className="form-group">
<label htmlFor="Title">Title:</label>
<input name="Title" type="text" className="form-control" id={this.state.form.Title} placeholder="Enter Title" onChange={this.changeHandler} />
</div>
<div className="form-group">
<label htmlFor="Content">Content:</label>
<textarea type="Content" className="form-control" id={this.state.form.Content} placeholder="Content" name="Content" style={{ height: "300px" }} onChange={this.changeHandler}></textarea>
</div>
<div className="form-group">
<label htmlFor="Tags">Tags:</label>
<EditableTagGroup />
</div>
<button type="submit" className="btn btn-default">Post Question</button>
<button type="submit" className="btn btn-default">Discard</button>
</form>
</div>
)
}
}
export default AskQuestion;
SingleQuestion.jsx
import React, { Component } from 'react';
import './SingleQuestion.css';
class SingleQuestion extends Component {
constructor(props) {
super(props)
this.state = {
};
}
render() {
return (
<div class="question-container col-lg-10">
<div class="question-icons pull-left">
<div class="rating">
<i class="button rating-up fa fa-thumbs-o-up" aria-hidden="true"></i>
<span class="counter">0</span>
<i class="button rating-down fa fa-thumbs-o-down" aria-hidden="true"></i>
</div>
</div>
<div class="result-link pull-left" style={{ paddingLeft: "30px", paddingTop: "55px" }}>
<h1>{this.props.Title}</h1>
</div>
</div>
)
}
}
export default SingleQuestion;
I saw posts like how to share state but didn't help me. mostly i saw something like this
<SingleQuestion callback=*****/>
if I do like that where ever I use this <SingleQuestion ------/> that component will be rendered which i don't want to do. I am new to reactjs please
help me in this..
Thanks in advance!!
This is an example to pass data between parallel components in reactjs
// App.js
import React, { Component } from 'react';
import { Route, Switch, Redirect } from 'react-router-dom';
import SingleQuestion from './SingleQuestion';
import AskQuestion from './AskQuestion';
class App extends Component {
state = {
formData: null
}
callbackFormData = (formData) => {
console.log(formData);
this.setState({formData: formData});
}
render() {
return (
<Switch>
<Route path='/askQuestion' render={() => <AskQuestion callbackFormData={this.callbackFormData}/> } />
<Route path='/singleQuestion' render={() => <SingleQuestion formData={this.state.formData}/>} />
</Switch>
);
}
}
export default App;
//AskQuestion
import React, { Component } from "react";
import { withRouter } from 'react-router-dom';
class AskQuestion extends Component {
redirect = () => {
this.props.history.push("singleQuestion");
};
submitHandler = () => {
let title = document.getElementById('title').value;
if(title !== '')
this.props.callbackFormData(title);
this.redirect();
}
render() {
return (
<React.Fragment>
<input id="title" />
<button onClick={this.submitHandler}>Post Question</button>
</React.Fragment>
)
}
}
export default withRouter(AskQuestion);
// SingleQuestion.js
import React, { Component } from "react";
class SingleQuestion extends Component {
render() {
return <h1>Title:- {this.props.formData}</h1>;
}
}
export default SingleQuestion;
i hope it helps!
If you want to use state form in SingleQuestion component after called redirect, try this.
redirect = () => {
this.props.history.push('/SingleQuestion', {
form: this.state.form
});
}
After then check console.log(this.props.history.location.state.form)

React Component not updating after adding new value in store

So I am using react, redux and firebase for this small crud app, whenever a new employee is created I redirect to the home component which should display all the employees created including the new one. But the new employee isn't showing up after redirection from create employee. What seems to be the issue, essentially I want is for the Home component to update with the new data.
Home.js
import React, { Component } from 'react'
import { connect } from 'react-redux'
import EmployeesList from '../employees/EmployeesList'
import { firestoreConnect } from 'react-redux-firebase'
import { compose } from 'redux'
class Home extends Component {
render() {
const { employees } = this.props
return (
<div>
<EmployeesList employees={employees} />
</div>
)
}
}
const mapStateToProps = (state) => {
// console.log(state)
return ({
employees: state.firestore.ordered.employees
})
}
export default compose(
connect(mapStateToProps),
firestoreConnect([
{ collection: 'employees', orderBy: ['createdAt', 'desc'] }
])
)(Home)
CreateEmployee.js
import React, { Component } from 'react'
import { compose } from 'redux'
import { connect } from 'react-redux'
import { withRouter } from "react-router";
import { createEmployee } from '../../store/actions/employeeActions'
import { withStyles } from '#material-ui/core/styles';
import Button from '#material-ui/core/Button';
import TextField from '#material-ui/core/TextField';
import Typography from '#material-ui/core/Typography';
const styles = theme => ({
bt_create: {
margin: theme.spacing.unit,
padding: '10'
},
input: {
display: 'none',
},
});
class CreateEmployee extends Component {
state = {
name: '',
email: '',
department: '',
salary: ''
}
handleChange = e => {
this.setState({
[e.target.id]: e.target.value
})
}
handleSubmit = e => {
e.preventDefault()
// console.log(this.state)
// TODO store state data in db
this.props.createEmployee(this.state)
this.props.history.push({
pathname: '/'
})
}
render() {
return (
<div>
<br />
<Typography variant="h6" color="inherit">
Create new employee
</Typography>
<form onSubmit={this.handleSubmit}>
<TextField
id="name"
label="Name"
defaultValue=""
margin="normal"
onChange={this.handleChange}
/>
<br />
<TextField
id="email"
label="Email"
defaultValue=""
margin="normal"
onChange={this.handleChange}
/>
<br />
<TextField
id="department"
label="Department"
defaultValue=""
margin="normal"
onChange={this.handleChange}
/>
<br />
<TextField
id="salary"
label="Salary"
defaultValue=""
margin="normal"
onChange={this.handleChange}
/>
<br />
<br />
<Button type="submit" variant="contained" color="primary" className="bt_create">Create</Button>
</form>
</div>
)
}
}
const mapDispatchToProps = dispatch => {
return {
createEmployee: (employee) => dispatch(createEmployee(employee))
}
}
export default compose(
withStyles(styles),
withRouter,
connect(null, mapDispatchToProps)
)(CreateEmployee)
Create employee action
export const createEmployee = employee => {
return (dispatch, getState, { getFirebase, getFirestore }) => {
const firestore = getFirestore()
// TODO add employee here
firestore.collection('employees').add({
...employee,
createdAt: new Date(),
updatedAt: new Date()
}).then(() => {
dispatch({
type: 'CREATE_EMPLOYEE_SUCCESS',
employee: employee
})
}).catch((err) => {
dispatch({ type: 'CREATE_EMPLOYEE_ERROR', err })
})
}

Jest - Mocking a function call within the handleSubmit of a form

I am trying to write a test that mocks the calling of a function within the handleSubmit of a form, however, I am unable to show that the function has been called.
The form is as follows:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import signUp from '../../actions/users/sign_up';
import PropTypes from 'prop-types';
class Signup extends Component {
constructor (props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.showError = this.showError.bind(this);
}
handleChange(event) {
const target = event.target;
this.setState({ [ target.name ]: target.value });
}
handleSubmit(event) {
event.preventDefault();
this.props.signUp(this.state);
}
showError(type) {
if (this.state && this.state.error && this.state.error.data.errors[ type ]) {
return this.state.error.data.errors[ type ][ 0 ];
}
}
componentDidUpdate (prevProps, prevState) {
const props = this.props;
if (prevProps === props) {
return;
}
this.setState({
...props,
});
}
render () {
return (
<div className='container-fluid'>
<div className='row'>
<div className='col col-md-6 offset-md-3 col-sm-12 col-12'>
<div className='card'>
<div className='card-header'>
<h4>Sign Up</h4>
</div>
<div className='card-body'>
<form onSubmit={ this.handleSubmit } >
<div className="form-row">
<div className="form-group col-md-12">
<label htmlFor="email">Email</label>
<input
type="email"
name="email"
className={ `form-control ${ this.showError('email') ? 'is-invalid' : '' }` }
id="email"
placeholder="Email"
onChange={ this.handleChange }
/>
<div className="invalid-feedback">
{ this.showError('email') }
</div>
</div>
</div>
<div className="form-row">
<div className="form-group col-md-12">
<label htmlFor="username">Username</label>
<input
type="text"
name="username"
className={ `form-control ${ this.showError('username') ? 'is-invalid' : '' }` }
id="username"
placeholder="Username"
onChange={ this.handleChange }
/>
<div className="invalid-feedback">
{ this.showError('username') }
</div>
</div>
</div>
<div className="form-row">
<div className="form-group col-md-12">
<label htmlFor="password">Password</label>
<input
type="password"
name="password"
className={ `form-control ${ this.showError('password') ? 'is-invalid' : '' }` }
id="password"
placeholder="Password"
onChange={ this.handleChange }
/>
<div className="invalid-feedback">
{ this.showError('password') }
</div>
</div>
<button type="submit" className="btn btn-primary">Sign Up</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
)
}
}
function mapStateToProps (state) {
return {
email: state.UsersReducer.email,
username: state.UsersReducer.username,
password: state.UsersReducer.password,
error: state.UsersReducer.error,
}
}
function mapDispatchToProps (dispatch) {
return bindActionCreators({
signUp: signUp,
}, dispatch);
}
Signup.propTypes = {
email: PropTypes.string,
username: PropTypes.string,
password: PropTypes.string,
signUp: PropTypes.func.isRequired
}
export default connect(mapStateToProps, mapDispatchToProps)(Signup);
The signUp action looks like this:
import { SIGN_UP, SHOW_USER_ERRORS } from '../types';
import axios from 'axios';
import { API_ROOT, setLocalStorageHeader } from './../../api-config';
import { push } from 'react-router-redux';
export default function signUp (params) {
return dispatch => {
axios.post(`${ API_ROOT }/auth.json`, params).then(res => {
setLocalStorageHeader(res);
dispatch(push('/profile'));
dispatch(signUpAsync(res.data));
}).catch(error => {
dispatch({ type: SHOW_USER_ERRORS, payload: { error: error.response } });
});
}
}
function signUpAsync (data) {
return {
type: SIGN_UP,
payload: data
};
}
I am trying to simulate the fact that the form will be submitted with the values obtained from the form inputs, which are in the form's state (email, username and password).
The test I currently have is:
import React from 'react';
import { shallow, mount } from 'enzyme';
import configureStore from 'redux-mock-store';
import { bindActionCreators } from 'redux';
import thunk from 'redux-thunk';
import Signup from '../../../components/users/signup';
import UsersReducer from '../../../reducers/reducer_users';
describe('<Signup />', () => {
describe('render()', () => {
test('submits the form data', async () => {
const mockStore = configureStore([thunk]);
const initialState = {
UsersReducer: {
email: '',
username: '',
password: '',
},
};
const store = mockStore(initialState);
const dispatchMock = jest.spyOn(store, 'dispatch');
const signUp = jest.fn();
const wrapper = shallow(<Signup store={store} signUp={signUp} />);
const component = wrapper.dive();
component.find('#email').simulate(
'change', {
target: {
name: 'email', value: 'foo#gmail.com'
}
}
);
component.find('#email').simulate(
'change', {
target: {
name: 'username', value: 'foo'
}
}
);
component.find('#password').simulate(
'change', {
target: {
name: 'password',
value: '1234567',
}
}
)
component.find('form').simulate(
'submit', {
preventDefault() {}
}
)
expect(dispatchMock).toHaveBeenCalled();
expect(signUp).toHaveBeenCalledWith({
email: 'foo#gmail.com',
username: 'foo',
password: '12345678'
});
});
});
});
But I keep getting the following error no matter what I try.
Expected mock function to have been called with:
[{"email": "foo#gmail.com", "password": "12345678", "username": "foo"}]
But it was not called.
I think it's due to the fact that signUp isn't being mocked properly in shallow(<Signup store={store} signUp={signUp} />) because when I do console.log(wrapper.props()) I get:
{
...
signUp: [Function],
...
}
rather than an indication that it's a mocked function:
{ [Function: mockConstructor]
_isMockFunction: true,
...
}
I know that the signUp action is being called by the dispatch of the test is passing. I can also see the params in the signUp action when I add a console.log(params) into it.
Any assistance would be greatly appreciated.
Your add signUp in the mapDispatchToProps when adding redux to the view.
As you use redux-mock-store you can access all actions that were called by store.getActions() So in your case, instead of passing a signUp as spy which will be overwritten by mapDispatchToProps, it could look like this:
const signUpCall = store.getActions()[0]
expect(signUpCall).toHaveBeenCalledWith({
email: 'foo#gmail.com',
username: 'foo',
password: '12345678'
});
So, after a lot of trial and error, the solution was to mock the action call itself which was done by adding import * as signUp from '../../../actions/users/sign_up'; and mocking it with const signUpActionMock = jest.spyOn(signUp, 'default');
The test now looks like this:
import React from 'react';
import { shallow } from 'enzyme';
import configureStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import Signup from '../../../components/users/signup';
import UsersReducer from '../../../reducers/reducer_users';
// Turns out this import allowed the signUp action to be mocked
import * as signUp from '../../../actions/users/sign_up';
describe('<Signup />', () => {
describe('render()', () => {
test('submits the form data', () => {
const middlewares = [thunk]
// Mock the signUp action call
const signUpActionMock = jest.spyOn(signUp, 'default');
const mockStore = configureStore(middlewares);
const initialState = {
UsersReducer: {
email: '',
username: '',
password: '',
},
};
const store = mockStore(initialState);
const wrapper = shallow(<Signup store={store} />);
const component = wrapper.dive();
component.find('#email').simulate(
'change', {
target: {
name: 'email', value: 'foo#gmail.com'
}
}
);
component.find('#email').simulate(
'change', {
target: {
name: 'username', value: 'foo'
}
}
);
component.find('#password').simulate(
'change', {
target: {
name: 'password',
value: '12345678',
}
}
);
component.find('form').simulate(
'submit', {
preventDefault() {}
}
);
expect(signUpActionMock).toHaveBeenCalledWith({
email: 'foo#gmail.com',
username: 'foo',
password: '12345678'
});
});
});
});

Resources