I have this class:
import React from 'react';
import {Link} from 'react-router';
import '../../styles/about-page.css';
import Item from './Item';
// Redux
import { connect } from 'react-redux';
import actions from '../../redux/actions';
import { bindActionCreators } from 'redux';
// Auth
import Auth from '../../modules/Auth';
import User from '../../constants';
class CommentForm extends React.Component {
constructor(props) {
super(props);
this.state = {
value: ''
};
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit() {
// alert('A name was submitted: ' + this.state.value);
this.props.onFinish({
name: this.props.user.name,
userId: this.props.user.id,
comment: this.state.value
});
}
render() {
if (!this.props.user || !this.props.user.name) return <div/>;
return (<div className="item">
{this.props.user.name}:
<label style={{width: '60%', margin: '10px'}}>
<input style={{width: '100%'}} type="text" value={this.state.value} onChange={this.handleChange.bind(this)} />
</label>
<input style={{width: '16%', display: 'inline-block', margin: '10px'}} type="submit" value="Enviar" onClick={this.handleSubmit.bind(this)}/>
</div>
);
}
}
#connect((state) => state)
class ItemPage extends React.Component {
constructor(props) {
super(props);
this.state = {
user: null,
pret: null
};
}
componentWillMount() {
let self = this;
this.props.actions.getPret(this.props.routeParams.id).then(function(a) {
self.setState({
pret: self.props.pret
})
});
if (Auth.isUserAuthenticated()) {
User.getBearer(Auth, function(info) {
self.setState({user: info});
});
}
}
onFinish(comment) {
//changing the state in react
//need to add '6' in the array
//create a copy of this.state.a
//you can use ES6's destructuring or loadash's _.clone()
const currentStatePretCopy = Object.assign({}, this.state.pret, { b: this.state.pret.comments.concat([comment])})
console.log(1, currentStatePretCopy);
currentStatePretCopy.comments.push(comment);
this.props.actions.updatePret(currentStatePretCopy);
}
render() {
let self = this;
if (!this.state || !this.state.pret) return <div/>;
return (<div>
<section>
<Item full={true} user={this.state.user} item={this.state.pret} actions={this.state.actions}/>
</section>
<div>
<CommentForm user={this.state.user} pret={this.state.pret} onFinish={this.onFinish.bind(this)}/>
{/* TODO: ad here */}
{this.state.pret.comments && this.state.pret.comments.length ? this.state.pret.comments.map(function (comment, index) {
return (<div className="item" key={index}> by <Link to={'/user/' + comment.userId}> #{comment.name} </Link> : {comment.comment} </div>);
}) : null}
</div>
</div>
);
}
}
function mapStateToProps (state) {
return {
pret: state.prets[0]
};
}
function mapDispatchToProps (dispatch) {
return {
actions: bindActionCreators(actions, dispatch)
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(ItemPage);
When i want to update the object, since props should be immutable, the documentation suggest cloning the object and put it in the state.
I am modifying the state with the new value and sending it to Redux actions but the system complains:
Uncaught Error: A state mutation was detected between dispatches, in the path 'prets.0.comments.1'. This may cause incorrect behavior.
Since i copy the object I do not know how should I update the store via React+Redux
The comments array is still the original one. So you are mutating the original object with push.
Replace
currentStatePretCopy.comments.push(comment);
With
currentStatePretCopy.comments = currentStatePretCopy.comments.concat([comment])
And everything should work fine
Related
So I am quite new to React world, and I have this problem I am trying to solve, but I don't quite understand why it is happening.
So I want to pass the state of component to parent component and from parent component to child component and everything look okay, and in console log the state goes trough, but nothing changes. I believe there is a way I need to listen for state change or something within child component so it works. If I put true in the parent component, child component also get's true, but if I toggle it on click, it goes trough but nothing changes in the child component.
Also I understand my code is little rough right now ill reafactor it later, but right now I am trying to understand why it does not work.
If anyone could help me I would be thankful for it.
This is component that controls the state.. So the state passes from TurnOnBtn to App and from App it goes to TodoList
import "./Todo.css";
class TurnOnBtn extends Component {
constructor(props) {
super(props);
this.state = { display: false };
this.handleState = this.handleState.bind(this);
}
handleState() {
this.setState({ display: !this.state.display });
this.props.checkDisplay(this.state.display);
}
render() {
return (
<button onClick={this.handleState} className="TurnOnBtn">
<i className="fa fa-power-off"></i>
</button>
);
}
}
export default TurnOnBtn;
parent component App
import TurnOnBtn from "./TurnOnBtn";
import TheMatrix from "./TheMatrxHasYou";
import TodoList from "./TodoList";
import { Component } from "react";
class App extends Component {
constructor(props) {
super(props);
this.state = { display: true };
this.checkDisplay = this.checkDisplay.bind(this);
}
checkDisplay(newDisplay) {
this.setState({
display: newDisplay,
});
console.log(this.state);
}
render() {
return (
<div className="App">
<TodoList display={this.state.display} />
<TheMatrix />
<TurnOnBtn checkDisplay={this.checkDisplay} />
</div>
);
}
}
export default App;
child component TodoList
import Todo from "./Todo";
import NewTodoForm from "./NewTodoForm";
import { v4 as uuid } from "uuid";
import "./Todo.css";
class TodoList extends Component {
constructor(props) {
super(props);
this.state = {
todos: [],
displayOn: this.props.display,
};
this.newTodo = this.newTodo.bind(this);
this.editTodo = this.editTodo.bind(this);
this.deleteTodo = this.deleteTodo.bind(this);
}
editTodo(id, updatedTask) {
const updatedTodo = this.state.todos.map((todo) => {
if (todo.id === id) {
return { ...todo, todo: updatedTask };
}
return todo;
});
this.setState({
todos: updatedTodo,
});
console.log(updatedTask);
}
deleteTodo(id) {
this.setState({
todos: this.state.todos.filter((todo) => todo.id !== id),
});
}
newTodo(newState) {
this.setState({
todos: [...this.state.todos, { ...newState }],
});
}
render() {
return (
<div
style={this.state.displayOn ? { opacity: 1 } : { opacity: 0 }}
className="Todo-screen"
>
{" "}
<div className="TodoList">
<div className="TodoList-todos">
{" "}
{this.state.todos.map((todo) => (
<Todo
key={uuid()}
id={todo.id}
active={todo.active}
editTodo={this.editTodo}
deleteTodo={this.deleteTodo}
todoItem={todo.todo}
/>
))}
</div>
</div>{" "}
<NewTodoForm newTodo={this.newTodo} />
</div>
);
}
}
export default TodoList;
The bug here is in these line of codes:
handleState() {
this.setState({ display: !this.state.display });
this.props.checkDisplay(this.state.display);
}
Remember setState is an async function, so by the time you set a new state using setState, the value for this.state is not guaranteed changed.
One way to fix this is using the setState callback, which will run after the state is changed:
handleState() {
this.setState({ display: !this.state.display }, function() {
this.props.checkDisplay(this.state.display);
});
}
But you don't need to use another state to keep display state in TurnOnBtn as you can pass the toggle callback from the parent:
App.js
class App extends Component {
constructor(props) {
super(props);
this.state = { display: true };
this.toggleDisplay = this.toggleDisplay.bind(this);
}
toggleDisplay() {
this.setState({
display: !this.state.display,
});
}
render() {
return (
<div className="App">
<TodoList display={this.state.display} />
<TheMatrix />
<TurnOnBtn toggleDisplay={this.toggleDisplay} />
</div>
);
}
}
TurnOnBtn.js
class TurnOnBtn extends Component {
constructor(props) {
super(props);
this.handleState = this.handleState.bind(this);
}
handleState() {
this.props.toggleDisplay();
}
render() {
return (
<button onClick={this.handleState} className="TurnOnBtn">
<i className="fa fa-power-off"></i>
</button>
);
}
}
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.
I am trying to save the value of the button as a string.If i click residence button it will save the value in categoryName as 'residence' or 'commercial' and redirect to another page .I have built a Rest API in the backend to bind and save the value in database.The code is something like this
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
class CustomizedButtons extends React.Component {
constructor(props) {
super(props);
this.state = {
apiUrl:config.publicRuntimeConfig.publicRuntimeConfigValue.apiUrl,
category: " ",
};
}
saveValue = () => {
console.log('savecategory');
axios.post( this.state.apiUrl+'/api/v1/leadsurvey/category', {
'categoryName':this.state.category,
}, {})
};
render() {
const { classes} = this.props;
return (
<div>
<div>
<p>What is the type of your property?</p>
<div>
<button onClick={() => this.saveValue()}>Residence</button>
<button onClick={() => this.saveValue()}>Commercial</button>
</div>
<div style={{marginTop: '90px'}}>
</div>
</div>
</div>
);
}
}
export default CustomizedButtons;
I am not getting how to make it work to bind and save.In case of saving form value i did something like this.
this.state = {
apiUrl:config.publicRuntimeConfig.publicRuntimeConfigValue.apiUrl,
FreeQuoteName :"",
};
this.handleFreeQuoteName = this.handleFreeQuoteName.bind(this);
saveFreeQuote = () => {
console.log('saveFreeQuote ...', this.state);
axios.post( this.state.apiUrl+'/api/v1/SalesLead/save', {
'name': this.state.FreeQuoteName,
}
}
handleFreeQuoteName(event) { this.setState({ FreeQuoteName: event.target.value }); }
<Form>
<p>Name*</p>
<input maxLength="30" onChange={this.handleFreeQuoteName} value={this.state.FreeQuoteName}
type="text" placeholder="Enter name here"/>
<div style={{textAlign:'center', marginTop:'35px', marginBottom:'22px'}} className={card.disable}>
<button disabled={isDisabled} type="button" fullwidth="true" variant="contained"
onClick={() => this.saveFreeQuote()} style={{padding: '9px 0px'}}>Submit</button>
</Form>
I want to do same for the value button.If i click the button it will save the value as a string and redirect to another page.How can i do it?
from your post I assumed that you want to save button value in state and also want to initiate the axios request while button click.
try to change like below
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import axios from 'axios';
class CustomizedButtons extends React.Component {
constructor(props) {
super(props);
this.state = {
apiUrl:config.publicRuntimeConfig.publicRuntimeConfigValue.apiUrl,
category: "",
};
}
saveValue = (e) => {
console.log('savecategory', e.target.innerHTML);
this.setState({
category: e.target.innerHTML
}, this.makeAxiosRequest);
};
makeAxiosRequest = () => {
axios.post( this.state.apiUrl+'/api/v1/leadsurvey/category', {
'categoryName':this.state.category,
}, {})
};
render() {
const { classes} = this.props;
return (
<div>
<div>
<p>What is the type of your property?</p>
<div>
<button onClick={this.saveValue}>Residence</button>
<button onClick={this.saveValue}>Commercial</button>
</div>
<div style={{marginTop: '90px'}}>
</div>
</div>
</div>
);
}
}
export default CustomizedButtons;
here am using callback function inside setState() to initiate axios request after button value saved in state.
Hope this helps.
I'm creating a simple CRUD React app that let's you manage products. Products only have a name and a price.
In my AddProduct component, my onSubmit method can successfully log this.nameInput.value, this.priceInput.value, but when I change the log to this.props.onAdd I get this.props.onAdd is not a function.
I'm following a tutorial, so I'm sure I'm missing one small thing, but could use another set of eyes on my code.
Here's my addProduct component - the onSubmit method has the this.props.onadd(...):
import React, { Component } from 'react';
class AddProduct extends Component {
constructor(props) {
super(props);
this.onSubmit = this.onSubmit.bind(this);
}
onSubmit(event) {
event.preventDefault();
this.props.onAdd(this.nameInput.value, this.priceInput.value);
}
render() {
return (
<form onSubmit={this.onSubmit}>
<h3>Add Product</h3>
<input placeholder="Name" ref={nameInput => this.nameInput = nameInput}/>
<input placeholder="Price" ref={priceInput => this.priceInput = priceInput}/>
<button>Add</button>
<hr />
</form>
);
}
}
And here's my App.js:
import React, { Component } from 'react';
import './App.css';
import ProductItem from './ProductItem';
import AddProduct from './AddProduct';
const products = [
{
name: 'iPad',
price: 200
},
{
name: 'iPhone',
price: 500
}
];
localStorage.setItem('products', JSON.stringify(products));
class App extends Component {
constructor(props) {
super(props);
this.state = {
products: JSON.parse(localStorage.getItem('products'))
};
this.onAdd = this.onAdd.bind(this);
this.onDelete = this.onDelete.bind(this);
}
componentWillMount() {
const products = this.getProducts();
this.setState({ products });
}
getProducts() {
return this.state.products;
}
onAdd(name, price) {
const products = this.getProducts();
products.push({
name,
price
});
this.setState({ products })
}
onDelete(name) {
const products = this.getProducts();
const filteredProducts = products.filter(product => {
return product.name !== name;
});
this.setState({ products: filteredProducts });
}
render() {
return (
<div className="App">
<h1>Products Manager</h1>
<AddProduct
/>
{
this.state.products.map(product => {
return (
<ProductItem
key={product.name}
{...product}
onDelete={this.onDelete}
/>
);
})
}
</div>
);
}
}
export default App;
What's the matter with my code? When I click the Add button, I get the error.
It's because you pass no prop to the <AddProduct /> you render.
You should add it like so:
<AddProduct onAdd={this.onAdd}/>
You need to pass the props to the component:
<AddProduct onAdd={onAdd} />
Just change the following line
<form onSubmit={this.onSubmit}>
to
<form onSubmit={this.onSubmit.bind(this)}>
onAdd is a field in the props of AddProduct. It means you should delivery a function to AddProduct in App. like: <AddProduct onAdd={() => console.log('works')} />
I am new to react native,I have a block of code for a form in react, need to convert this into react native elements using the same let variable AddTodo
let AddTodo = ({ dispatch }) => {
let input
return (
<div>
<form onSubmit={e => {
e.preventDefault()
if (!input.value.trim()) {
return
}
dispatch(addTodo(input.value))
input.value = ''
}}>
<input ref={node => {
input = node
}} />
<button type="submit">
Add Todo
</button>
</form>
</div>
)
}
AddTodo = connect()(AddTodo)
Can anyone help me here? This is the source code
Here is simple code for react-native, that convert from your react code.
You can find more about react-native here.
in html we use for group other element so for react-native using
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
class AddTodo extends Component {
constructor(props) {
super(props);
this.state = {
todoText: ''
};
}
render() {
return (
<View>
<TextInput
onChangeText={(todoText) => this.setState({todoText})}
value={this.state.todoText}
/>
<TouchableHighlight onPress={() => {
if(this.state.todoText) {
this.props.addTodo(input.value);
this.setState({ todoText: '' })
}
}}>
<Text>Add Todo</Text>
</TouchableHighlight>
</View>
);
}
}
function mapStateToProps(state) {
return {};
}
function mapDispatchToProps(dispatch) {
return {
addTodo: bindActionCreators(addTodo, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(AddTodo);
You can find react-native redux todo example here