not able to share state between two components in react - reactjs

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)

Related

React not re-rendering after state change

import React, {Component, Fragment} from 'react'
import axios from 'axios'
import PropTypes from "prop-types";
import { connect } from 'react-redux'
import { postComment } from "../../../actions/comments"
import './styles.scss'
export class Comments extends Component {
constructor(props) {
super(props)
this.state = {
comment: "",
author: null
}
}
static propTypes = {
postComment: PropTypes.func.isRequired
};
componentDidMount() {
axios
.get(`/api/auth/user`)
.then(res => {
const author = res.data.id
this.setState({author});
})
.catch(err => console.log(err));
}
onSubmit(e) {
e.preventDefault();
const variables = {
content: this.state.comment,
post: this.props.post.id,
author: this.state.author
}
this.props.postComment(variables)
this.setState({content: ""});
}
onFieldChange(fieldName) {
return function (event) {
this.setState({[fieldName]: event.target.value});
}
}
render() {
return (
<Fragment>
<div class="input">
<form style={{ display:'flex'}} onSubmit={e => this.onSubmit(e)}>
<div class="input-group">
<input
class="form-control rounded-corner"
style={{width: '100%', borderRadius: '5px'}}
onChange={this.onFieldChange('comment').bind(this)}
value = {this.state.comment}
placeholder="Leave a comments"
/>
<span class="input-group-btn p-l-10">
<button class="btn btn-primary f-s-12 rounded-corner">Comment</button>
</span>
</div>
</form>
</div>
<div>
{/* Comment Lists */}...
))}
</div>
</Fragment>
);
}
}
export default connect(
null,
{ postComment }
)(Comments);
This is a page when I add comments to the posts, which works fine but it doesn't update immediately and I have to refresh. The componentDidMount method uses axios to get current user for setting the author for the current comment. I'm changing the state after the form submit, I can't see what the issue is.

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.

New Component is returned but the previous component stays the same in ReactJs

A new component is returned after login, but both the login component and the Home Component are seen on the page. I need to return the Home Componenet without Login Component. I am new to React and still trying to understand return and routes in React.
This is my pages component which returns either Login or Home based on this.state.redirect1.
import React, { Component } from 'react';
import { Redirect } from 'react-router-dom';
import '../../App.css';
import Login from '../Login';
import Home from '../Home';
import Header from './Header';
import Footer from './Footer';
class Pages extends Component {
constructor(props) {
super(props)
this.state = {
redirect: false,
}
}
handleClick() {
this.state.redirect = true;
console.log(this.state.redirect);
}
changeRedirect =() =>{
this.state.redirect = true;
console.log(this.state.redirect);
this.forceUpdate()
}
renderRedirect = () => {
if(this.props.redirect1){
return <Home/>
}
else{
return <Login/>
}
}
render() {
return (
<div className="mh-100 PgWidth">
{this.renderRedirect()}
</div>
)
}
}
export default Pages
Below is my Login, Home and App Components
Login.js
import React, { Component } from 'react'
import Axios from 'axios';
import Pages from './common/Pages'
import { Redirect } from 'react-router-dom';
class Login extends Component {
constructor(props) {
super(props)
this.state = {
username: '',
password: '',
redirect: false
}
}
handleUsername = (event) => {
this.setState({
username: event.target.value
})
}
handlePassword = (event) => {
this.setState({
password: event.target.value
})
}
renderRedirect = () => {
if (this.state.redirect) {
console.log("from render redirect");
return <Pages redirect1={this.state.redirect} />
}
}
formSubmitHandler = event => {
let formdata = new FormData();
formdata.append("username", this.state.username);
formdata.append("password", this.state.password);
Axios.post("/auth/local",{
"name":this.state.username,
"password": this.state.password
})
.then(res => {
if (res) {
console.log(res);
this.setState({ redirect: true });
}
})
event.preventDefault() // used to keep the form data as entered even after the submit
}
render() {
const { username, password } = this.state
return (
<div className="p-5">
{ this.renderRedirect() }
<h3>Sign-In</h3>
<form onSubmit={this.formSubmitHandler}>
<div className="form-group row">
<label htmlFor="inputEmail3" className="col-sm-2 col-form-label">Username</label>
<div className="col-sm-10">
<input type="text" value={username} onChange={this.handleUsername}
className="form-control" id="inputEmail3" placeholder="Username" />
</div>
</div>
<div className="form-group row">
<label htmlFor="inputPassword3" className="col-sm-2 col-form-label">Password</label>
<div className="col-sm-10">
<input type="password" value={password} onChange={this.handlePassword}
className="form-control" id="inputPassword3" placeholder="Password" />
</div>
</div>
<div className="form-group row">
<div className="col-sm-2">Checkbox</div>
<div className="col-sm-10">
<div className="form-check">
<input className="form-check-input" type="checkbox" id="gridCheck1" />
<label className="form-check-label" htmlFor="gridCheck1">
Example checkbox
</label>
</div>
</div>
</div>
<div className="form-group row">
<div className="col-sm-10">
<button type="submit" onClick={this.formSubmitHandler} className="btn btn-primary">Sign in</button>
</div>
</div>
</form>
</div>
)
}
}
export default Login
Home.js
import React, { Component } from 'react'
import '../App.css';
export class Home extends Component {
componentDidMount(){
console.log("home component mount");
}
render() {
return (
<div>
<h1>The page has been routed</h1>
</div>
);
}
}
export default Home
App.js
import React, { Component } from 'react';
import './App.css';
import Header from './components/common/Header';
import Footer from './components/common/Footer';
import Pages from './components/common/Pages';
class App extends Component {
render() {
return (
<div className="App container-fluid bg-light w-75">
<div className="row justify-content-md-center">
<div className="col m-0 p-0">
<Header/>
<div className="">
<Pages/>
</div>
<Footer/>
</div>
</div>
</div>
);
}
}
export default App;
Issue is in this line:
{ this.renderRedirect() }
Once redirect will be true, it will render the Home page first then the Login component.
Solution to you problem is: Manage the redirect bool in Page component only, and pass a function to update to Login component to update its value and decide the component based on that.
Changes:
1- defined redirect: false in Pages component.
2- A function to change its value in Pages component:
updateValue = (value) => {
this.setState({ redirect: true })
}
3- Pass function to Login component:
renderRedirect = () => {
if(this.props.redirect1) {
return <Home/>
}
else{
// =====> here
return <Login updateValue={this.updateValue} />
}
}
4- After successful Login call this function and render Home Component:
formSubmitHandler = event => {
event.preventDefault();
let formdata = new FormData();
formdata.append("username", this.state.username);
formdata.append("password", this.state.password);
Axios.post("/auth/local",{
"name":this.state.username,
"password": this.state.password
})
.then(res => {
if (res) {
// =======> here
this.props.updateValue(true)
}
})
}
5- Remove this line from Login Component:
{ this.renderRedirect() }
Problem with current code:
You are managing the login session using state variable, so after refreshing the page it will again show the login page not home page. So better to store the value in localStorage and read its value in page component to decide the initial value of redirect.
Suggestion:
Instead of deciding the route/component using boolean, better to use react-router for better structuring/managing the app.
Try return it in render:
import React, { Component } from 'react';
import { Redirect } from 'react-router-dom';
import '../../App.css';
import Login from '../Login';
import Home from '../Home';
import Header from './Header';
import Footer from './Footer';
class Pages extends Component {
constructor(props) {
super(props)
this.state = {
redirect: false,
}
}
handleClick() {
this.state.redirect = true;
console.log(this.state.redirect);
}
changeRedirect =() =>{
this.state.redirect = true;
console.log(this.state.redirect);
this.forceUpdate()
}
render() {
if(this.props.redirect){
return (
<div className="mh-100 PgWidth">
<Home/>
</div>
)
} else {
return (
<div className="mh-100 PgWidth">
<Login/>
</div>
)
}
}
export default Pages;
You can do it like this
<div className="mh-100 PgWidth">
{this.props.redirect1&&<Home/>}
{!this.props.redirect1&&<Login />}
</div>
But, the best way to do this is using react router and managing the global react state

Render React component in axios

I have just started learning React and I'm trying to make a simple application for searching vacancies using third-party server API. The application consists of form with one input, on submit it sends a request to server using axios, gets a response and must render it.
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import "bootstrap/dist/css/bootstrap.css";
import axios from 'axios';
const instance = axios.create({
baseURL: 'https://api.hh.ru/vacancies/',
headers: {
'User-Agent': 'React App/1.0 (tatyana.fomina.1986#gmail.com)',
'HH-User-Agent': 'React App/1.0 (tatyana.fomina.1986#gmail.com)'
}
});
const Header = () => {
return <h1>Поиск вакансий на HH.ru</h1>
}
const Vacancies = props => {
return <div>Some Text</div>
}
class SearchForm extends React.Component {
constructor(props) {
super(props)
this.state = {
position: ''
}
this.handlePositionChange = this.handlePositionChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handlePositionChange(e) {
this.setState({
[e.target.name]: e.target.value
});
}
handleSubmit(e) {
e.preventDefault();
var formButton = document.getElementById('form-button');
formButton.disabled = true;
var position = this.state.position;
console.log(position);
if ( position ) {
instance({
method: 'GET',
url: '?text=' + position,
data: {
position: position
}
})
.then(function(response) {
console.log(response.data);
formButton.disabled = false;
})
.catch(function (error) {
console.log(error);
});
} else {
formButton.disabled = false;
}
}
render() {
return (
<form className="form search-form" onSubmit = { this.handleSubmit }>
<div className="form-row">
<div className="form-group col-md-8">
<label htmlFor="position"> Position *
< /label>
<input type="text" className="form-control" name="position" id="position" placeholder="Position" onChange={ this.handlePositionChange } value={ this.state.position } />
</div>
<div className="form-group col-md-4 d-flex flex-column justify-content-end">
<input id = 'form-button'
className = 'btn btn-primary'
type = 'submit'
placeholder = 'Send' / >
</div>
</div>
</form>
)
}
}
class App extends Component {
render() {
return (
<div className="container">
<div className="row">
<div className="col-12">
<Header />
<SearchForm />
<Vacancies />
</div>
</div>
</div>
);
}
}
export default App;
I have a problem with rendering <Vacancies />, is it possible to render it dynamically and update data every time on every new request and response from server?
What you want is for Vacancies to get the updated data which is what you get after an API request from SearchForm, in such a case you need to restructure your components and Lift the action up in the parent and pass the data as props to the Sibling components
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import "bootstrap/dist/css/bootstrap.css";
import axios from 'axios';
const instance = axios.create({
baseURL: 'https://api.hh.ru/vacancies/',
headers: {
'User-Agent': 'React App/1.0 (tatyana.fomina.1986#gmail.com)',
'HH-User-Agent': 'React App/1.0 (tatyana.fomina.1986#gmail.com)'
}
});
const Header = () => {
return <h1>Поиск вакансий на HH.ru</h1>
}
const Vacancies = props => {
return <div>Some Text</div>
}
class SearchForm extends React.Component {
constructor(props) {
super(props)
this.state = {
position: ''
}
this.handlePositionChange = this.handlePositionChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handlePositionChange(e) {
this.setState({
[e.target.name]: e.target.value
});
}
render() {
return (
<form className="form search-form" onSubmit = { (e) => this.handleSubmit(e, this.state.position) }>
<div className="form-row">
<div className="form-group col-md-8">
<label htmlFor="position"> Position *
< /label>
<input type="text" className="form-control" name="position" id="position" placeholder="Position" onChange={ this.handlePositionChange } value={ this.state.position } />
</div>
<div className="form-group col-md-4 d-flex flex-column justify-content-end">
<input id = 'form-button'
className = 'btn btn-primary'
type = 'submit'
disabled={this.props.disableSubmit}
placeholder = 'Send' / >
</div>
</div>
</form>
)
}
}
class App extends Component {
state = {
disableSubmit: false;
}
handleSubmit = (e, position) => {
e.preventDefault();
this.setState({disableSubmit : true});
console.log(position);
if ( position ) {
instance({
method: 'GET',
url: '?text=' + position,
data: {
position: position
}
})
.then(function(response) {
this.setState({data: response.data, disableSubmit:false});
})
.catch(function (error) {
console.log(error);
});
} else {
this.setState({disableSubmit : false});
}
}
render() {
return (
<div className="container">
<div className="row">
<div className="col-12">
<Header />
<SearchForm handleSubmit = {this.handleSubmit} disableSubmit={this.state.disableSubmit}/>
<Vacancies data={this.state.data}/>
</div>
</div>
</div>
);
}
}
export default App;
Also while using React you must make sure that you are not modifying the DOM element yourself and handle all Interactions the React way. For instance you can control the disabled state of your submit button using a prop or a state.

How to hookup input boxes to Reactjs / Redux?

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)}

Resources