I need to fire the action to update storyTextValue inside StoryTextReducer so I can use it as this.props.storyTextValue in SearchArticle.
In other words, in CreateArticle upon the user typing in <textarea> followed by clicking Submit, I want whatever they've typed in to be stored in storyTextValue which would allow me to use this.props.storyTextValue in SearchArticle to display the text.
What am I doing wrong and how can I achieve this?
Here's CreateArticle:
import React, { Component } from 'react';
import {connect} from "react-redux";
import * as actionType from "../../store/actions/actions";
class CreateArticle extends Component {
constructor(props) {
super(props);
}
handleSubmit = event => {
event.preventDefault();
this.setState({storyTextValue: event.target.storyTextValue});
this.props.storyTextValueRedux(event.target.storyTextValue);
}
handleStoryText = event => {
event.preventDefault();
this.setState({value: event.target.value});
}
onSubmit = () => {
if(this.props.storyTextValue === "") {
alert("Please enter the value and then click submit");
} else {
alert("Article saved " + '\n' + this.props.storyTextValue);
}
}
render() {
return(
<div>
<form onSubmit={this.handleSubmit}>
<input onChange={this.handleChange} value={this.props.cityCodeValue} type="text" placeholder="city code"/>
<input type="text" placeholder="author name"/>
<textarea value={this.props.storyTextValue} onChange={this.handleStoryText} rows="2" cols="25" />
<button type="submit" value="Submit" onClick={() => this.onSubmit()}>Submit</button>
</form>
</div>
);
}
}
const mapStateToProps = state => {
return {
articleIdValue: state.articleIdValue.articleIdValue,
storyTextValue: state.storyTextValue.storyTextValue
};
};
const mapDispatchToProps = dispatch => {
return {
articleIdValueRedux: (value) => dispatch({type: actionType.ARTICLE_ID_VALUE, value}),
storyTextValueRedux: (value) => dispatch({type: actionType.STORY_VALUE, value})
};
};
export default connect(mapStateToProps, mapDispatchToProps)(CreateArticle);
Here's SearchArticle:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actionType from '../../store/actions/actions';
class SearchArticle extends Component {
constructor(props) {
super(props);
this.state = {
flag: false
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
this.props.CityCodeReducerRedux(event.target.value);
}
handleSubmit(event) {
this.setState({flag: true});
event.preventDefault();
}
displayName = () => {
if(this.props.cityCodeValue === "nyc" || this.props.articleIdValue === 1) {
return(
<div>
<p>author name: {this.props.authorNameValue}</p>
<p>article text: {this.props.storyTextValue}</p> {/* want to display story text here */}
</div>
);
}
}
render() {
return(
<div>
<form onSubmit={this.handleSubmit}>
<input onChange={this.handleChange} value={this.props.cityCodeValue} type="text" placeholder="city code"/>
<input onChange={this.handleChange} value={this.props.articleIdValue} placeholder="article id"/>
<button onClick={() => this.displayName} value="Search">Submit</button>
{this.state.flag ? this.displayName() : null}
</form>
</div>
);
}
}
const mapStateToProps = state => {
return {
cityCodeValue: state.cityCodeValue.cityCodeValue,
authorNameValue: state.authorNameValue.authorNameValue,
articleIdValue: state.articleIdValue.articleIdValue,
storyTextValue: state.storyTextValue.storyTextValue
};
};
const mapDispatchToProps = dispatch => {
return {
CityCodeReducerRedux: (value) => dispatch({type: actionType.CITY_CODE_VALUE, value}),
articleIdValueRedux: (value) => dispatch({type: actionType.ARTICLE_ID_VALUE, value})
};
};
export default connect(mapStateToProps, mapDispatchToProps)(SearchArticle);
Here's StoryTextReducer:
import * as actionType from '../store/actions/actions';
const initialState = {
storyTextValue: ''
};
const StoryTextReducer = (state = initialState, action) => {
switch (action.type) {
case actionType.STORY_VALUE:
return {
...state,
storyTextValue: action.value
};
default:
return state;
}
};
export default StoryTextReducer;
In handleSubmit of CreateArticle please console.log(event.target.storyTextValue).... i would think it is undefined. I think you would prefer this.props.storyTextValueRedux(event.target.value);
Related
I'm currently working on a project that uses QuillJS for a rich text editor. I need to post the rich text content to my backend but I'm not sure how to access the QuillJS output.
In RichTextEditor.js
import React, { Component } from "react";
import ReactQuill from "react-quill";
import "react-quill/dist/quill.snow.css";
class RichTextEditor extends Component {
constructor(props) {
super(props);
// this.formats = formats;
this.state = { text: "" }; // You can also pass a Quill Delta here
this.handleChange = this.handleChange.bind(this);
}
handleChange(value) {
this.setState({ text: value });
const text = this.state;
console.log(text);
}
render() {
return (
<ReactQuill
value={this.state.text}
onChange={this.handleChange}
formats={this.formats}
modules={this.modules}
/>
);
}
}
export default RichTextEditor;
The console.log(text) basically just outputs the content of the rich text editor. Something like this "<p><em>aasdasdasd</em><strong><em>asdasdasdasd</em></strong></p>"
In Post.js
import React, { Component } from "react";
import RichTextEditor from "./RichTextEditor.js";
import "../../css/Post.css";
class Post extends Component {
constructor(props) {
super(props);
this.state = {
question: "",
};
}
onChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
console.log(this.state);
};
handleSubmit = (e) => {
e.preventDefault();
const { question } = this.state;
console.log("Question");
console.log(question);
render() {
const { question } = this.state;
return (
<div className="post">
<div className="post__container">
<form onSubmit={this.handleSubmit}>
<div className="post__richTextEditor">
<RichTextEditor value={question} onChange={this.onChange} name="question" />
</div>
</form>
</div>
</div>
);
}
}
export default Post;
I'm trying to update the state of the question but it doesn't seem to be updating. console.log(question) only outputs a single string.
How can I access the same string output from RichTextEditor.js?
Your RichTextEditor component should not handle change, it should only receive props from higher component:
class RichTextEditor extends Component {
constructor(props) {
super(props);
}
render() {
return (
<ReactQuill
value={this.props.value}
onChange={this.props.onChange}
formats={this.formats}
modules={this.modules}
/>
);
}
}
export default RichTextEditor;
Then your Post component pass value and onChange props to RichTextEditor:
class Post extends Component {
constructor(props) {
super(props);
this.state = {
question: "",
};
this.onChange = this.onChange.bind(this);
}
onChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
console.log(this.state);
};
handleSubmit = (e) => {
e.preventDefault();
const { question } = this.state;
console.log("Question");
console.log(question);
render() {
const { question } = this.state;
return (
<div className="post">
<div className="post__container">
<form onSubmit={this.handleSubmit}>
<div className="post__richTextEditor">
<RichTextEditor value={question} onChange={this.onChange} name="question" />
</div>
</form>
</div>
</div>
);
}
}
in RichTextEditor.js
handleChange(value) {
this.setState({ text: value });
const text = this.state;
console.log(text);
props.onChange(text); // passing the inner State to parent as argument to onChange handler
}
Now in Post.js
onChange = (newStateString) => {
//this.setState({ [e.target.name]: e.target.value });
console.log(newStateString); // you should get the string here
};
I have two pages on my react app. One page allows you to submit a post, and the second page shows all of the posts. I need to be able to retrieve the data from the state on one page, but I am receiving an error. What am I doing wrong to display this, because I thought I could use props to gather the state from my post page.
My Display Post Page:
import React from 'react';
import './App.css';
export default class Scroll extends React.Component {
render() {
return (
<div className="flex-container">
<div className="post">
{this.props.displayPost(this.props.state.posts)}
</div>
</div>
);
}
}
My post page:
import React from 'react';
import axios from 'axios';
import './App.css';
import { post } from '../../routes/routes';
export default class PersonList extends React.Component {
state = {
title: "",
body: "",
posts: []
};
componentDidMount = () => {
this.getPost();
}
getPost = () => {
axios.get("http://localhost:5000/posts/save")
.then((response) => {
const data = response.data;
this.setState({ posts: data });
console.log("Data has been recieved")
})
.catch(() => {
alert("Error recieving data")
})
}
handleChange = (event) => {
const target = event.target;
const name = target.name;
const value = target.value;
this.setState({
[name]: value
})
};
submit = (event) => {
event.preventDefault();
const payload = {
title: this.state.title,
body: this.state.body,
}
axios({
url: 'http://localhost:5000/posts/save',
method: 'POST',
data: payload,
})
.then(() => {
console.log('Data sent to the server');
})
.catch(() => {
console.log('Internal server error');
});
};
displayPost = (posts) => {
if (!post.length) return null;
return posts.map((post, index) => {
<div key={index}>
<h3 id="post-text">{post.title}</h3>
<p id="post-text">{post.body}</p>
</div>
});
}
render() {
console.log("State ", this.state)
return (
<div className="flex-container-home">
<div className="app">
<form onSubmit={this.submit}>
<input
placeholder="title"
type="text"
name="title"
value={this.state.title}
onChange={this.handleChange}
/>
<textarea placeholder="description"
name="body"
cols="30" rows="10"
value={this.state.body}
onChange={this.handleChange}
>
</textarea>
<button>Submit</button>
</form>
</div>
</div>
)
}
}
Here is working example:
import React from "react";
export default class PersonList extends React.Component {
state = {
title: "",
body: "",
posts: [],
};
componentDidMount = () => {
this.getPost();
};
getPost = () => {
this.setState({ posts: ["post1", "post2", "post3"] });
};
displayPost = (posts) => {
if (!posts || !posts.length) return null;
return posts.map((post, index) => (
<div key={index}>
<p>{post}</p>
</div>
));
};
render() {
return (
<div className="App">
<Scroll displayPost={this.displayPost} posts={this.state.posts} />
</div>
);
}
}
class Scroll extends React.Component {
render() {
return (
<div className="post">
Posts: {this.props.displayPost(this.props.posts)}
</div>
);
}
}
I am trying to get the user to input data for my TODO App instead of picking up data from a predefined array.
Below is my Parent component
import React from "react"
import TodoItem from "./TodoItem"
import todosData from "./todosData"
class App extends React.Component {
constructor() {
super()
this.state = {
name: "",
todos: todosData
}
this.handleChange = this.handleChange.bind(this)
this.nameEnter = this.nameEnter.bind(this)
}
handleChange(id) {
this.setState(prevState => {
const updatedTodos = prevState.todos.map(item => {
if (item.id === id){
return {
...item,
completed: !item.completed
}
}
return item
})
return {
todos: updatedTodos
}
})
}
nameEnter(){
var name = this.name
console.log(name)
}
render() {
const todoItems = this.state.todos.map(item => <TodoItem key={item.id} item={item}
handleChange = {this.handleChange} nameEnter= {this.nameEnter}/>)
return (
<div className="todo-list">
{todoItems}
</div>
)
}
}
export default App
This one is the child component where I have added input fields
import React from "react"
function TodoItem(props) {
return (
<div className="todo-item">
<input
type = "checkbox"
checked = {props.item.completed}
onChange = { () => props.handleChange(props.item.id) }
/>
<input type = "text" name= "name" />
<p>{props.item.text}</p>
</div>
)
}
export default TodoItem
This is how my page looks. Instead of predefined text example:"GROCERRY SHOPPING", I want the user to enter text and it should be in place of that.
Ok so here it is
<input onChange={(e)=>handleChange(e)}/>
//react hooks
const [values,setValues] = useState({inputValue: 'predefined'});
handleChange = (e)=>{
setValues({...values,[e.target.name]: e.target.value})
}
//classes
handleChange = (e)=>{
this.setState({[e.target.name]:e.target.value});
}
this generic and can apply to a number of inputs, not just one
import React from "react"
class Form extends React.Component {
constructor() {
super()
this.state = {
name: ""
}
}
onChange = (e) => {
this.setState({
[e.target.name]: e.target.value
})
}
render() {
const {name} = this.state
return (
<input type="text" name="name" value={name} onChange={this.onChange}/>
)
}
}
export default Form
changeInputVal (ev, index) {
ev.persist();
const newArr = this.state.inputArrVal;
newArr[index]=ev.target.value;
this.setState({inputArrVal:newArr, currentPage:1});
}
you need to do something like that in TodoItem --
where --
1- inputArrVal:{} is object And
2- "this.changeInputVal(event, props.item.id)}/>"
Whenever the user types something in the <textarea> and then clicks submit, I want to display what they've written in the alert() via handleStorytext() function.
How can I achieve this and what did I do wrong?
As of now, upon writing something in the <textarea> and then clicking submit, I an get error which points to handleStorytext() saying: TypeError: Cannot read property 'setState' of undefined.
Here's CreateArticle:
import React, { Component } from 'react';
import {connect} from "react-redux";
import * as actionType from "../../store/actions/actions";
class CreateArticle extends Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
this.props.articleIdValueRedux(event.target.value);
}
handleSubmit(event) {
this.setState({value: event.target.value});
this.props.storyTextValueRedux(event.target.value);
event.preventDefault();
}
handleStoryText(event) {
this.setState({value: event.target.value});
this.props.storyTextValueRedux(event.target.value);
alert("Article saved " + '\n' + this.props.storyTextValue);
event.preventDefault();
}
render() {
return(
<div>
<form onSubmit={this.handleSubmit}>
<input onChange={this.handleChange} value={this.props.cityCodeValue} type="text" placeholder="city code"/>
<input type="text" placeholder="author name"/>
<textarea value={this.props.storyTextValue} onChange={this.handleStoryText} rows="2" cols="25" />
<input type="submit" value="Submit"/>
</form>
</div>
);
}
}
const mapStateToProps = state => {
return {
articleIdValue: state.articleIdValue.articleIdValue,
storyTextValue: state.storyTextValue.storyTextValue
};
};
const mapDispatchToProps = dispatch => {
return {
articleIdValueRedux: (value) => dispatch({type: actionType.ARTICLE_ID_VALUE, value}),
storyTextValueRedux: (value) => dispatch({type: actionType.STORY_VALUE, value})
};
};
export default connect(mapStateToProps, mapDispatchToProps)(CreateArticle);
Here's ArticleIdReducer:
import * as actionType from '../store/actions/actions';
const initialState = {
storyTextValue: ''
};
const StoryTextReducer = (state = initialState, action) => {
switch (action.type) {
case actionType.STORY_VALUE:
return {
...state,
storyTextValue: action.value
};
default:
return state;
}
};
export default StoryTextReducer;
Few things you are doing incorrect
You are using function in textarea onChange but you are not binding it. You
need to bind it in constructor always and never bind a function
directly in render or anywhere else except constructor
you need to assign this.state.value to value prop in textarea but you are assigning a prop which isn't exist
Also when you do setState the updated state value won't be available immediately before render
The below code works for you in ES5
import React, { Component } from 'react';
import {connect} from "react-redux";
import * as actionType from "../../store/actions/actions";
class CreateArticle extends Component {
constructor(props) {
super(props);
this.state = {
value: "",
error: ""
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleStoryText = this.handleStoryText.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
this.props.articleIdValueRedux(event.target.value);
}
handleSubmit(event) {
this.setState({value: event.target.value});
this.props.storyTextValueRedux(event.target.value);
event.preventDefault();
}
handleStoryText(event) {
event.preventDefault();
this.setState({value: event.target.value});
}
onSubmit = () => {
if(this.state.value === ""){
alert("Please enter the value and then click submit");
}else{
alert("Article saved " + '\n' + this.state.value);
}
}
render() {
const { value } = this.state;
return(
<div>
<form onSubmit={this.handleSubmit}>
<input onChange={this.handleChange} value={this.props.cityCodeValue} type="text" placeholder="city code"/>
<input type="text" placeholder="author name"/>
<textarea value={value} onChange={this.handleStoryText} rows="2" cols="25" />
<input type="submit" value="Submit" onclick={this.onSubmit}/>
</form>
</div>
);
}
}
const mapStateToProps = state => {
return {
articleIdValue: state.articleIdValue.articleIdValue,
storyTextValue: state.storyTextValue.storyTextValue
};
};
const mapDispatchToProps = dispatch => {
return {
articleIdValueRedux: (value) => dispatch({type: actionType.ARTICLE_ID_VALUE, value}),
storyTextValueRedux: (value) => dispatch({type: actionType.STORY_VALUE, value})
};
};
export default connect(mapStateToProps, mapDispatchToProps)(CreateArticle);
If you hate doing binding in constructor thn use arrow functions. The advantage with arrow function is you no need to bind a function and no need to refer this to a local variable like self for eg. this context is by default available in arrow function
ES6 version code
import React, { Component } from 'react';
import {connect} from "react-redux";
import * as actionType from "../../store/actions/actions";
class CreateArticle extends Component {
constructor(props) {
super(props);
this.state = {
value: ""
}
}
handleChange = event => {
this.setState({value: event.target.value});
this.props.articleIdValueRedux(event.target.value);
}
handleSubmit = event => {
event.preventDefault();
this.setState({value: event.target.value});
this.props.storyTextValueRedux(event.target.value);
}
handleStoryText = event => {
event.preventDefault();
this.setState({value: event.target.value});
}
onSubmit = () => {
if(this.state.value === ""){
alert("Please enter the value and then click submit");
}else{
alert("Article saved " + '\n' + this.state.value);
}
}
render() {
const { value } = this.state;
return(
<div>
<form onSubmit={this.handleSubmit}>
<input onChange={this.handleChange} value={this.props.cityCodeValue} type="text" placeholder="city code"/>
<input type="text" placeholder="author name"/>
<textarea value={value} onChange={this.handleStoryText} rows="2" cols="25" />
<input type="submit" value="Submit" onclick={this.onSubmit}/>
</form>
</div>
);
}
}
const mapStateToProps = state => {
return {
articleIdValue: state.articleIdValue.articleIdValue,
storyTextValue: state.storyTextValue.storyTextValue
};
};
const mapDispatchToProps = dispatch => {
return {
articleIdValueRedux: (value) => dispatch({type: actionType.ARTICLE_ID_VALUE, value}),
storyTextValueRedux: (value) => dispatch({type: actionType.STORY_VALUE, value})
};
};
export default connect(mapStateToProps, mapDispatchToProps)(CreateArticle);
when you pass onChange handler functions, the scope of 'this' is lost.
So, use onChange={this.handleStoryText.bind(this)} in your render
function
For more info, you can check these docs
import React, { Component } from 'react'; import {connect} from
"react-redux"; import * as actionType from
"../../store/actions/actions";
class CreateArticle extends Component {
constructor(props) {
super(props);
this.state = {storyTextValue:""}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
this.props.articleIdValueRedux(event.target.value);
}
handleSubmit(event) {
this.setState({value: event.target.value});
this.props.storyTextValueRedux(event.target.value);
alert("Article saved " + '\n' + this.state.storyTextValue);
event.preventDefault();
}
handleStoryText(event) {
this.setState({storyTextValue: event.target.value});
this.props.storyTextValueRedux(event.target.value);
event.preventDefault();
}
render() {
return(
<div>
<form onSubmit={this.handleSubmit.bind(this)}>
<input onChange={this.handleChange} value={this.props.cityCodeValue} type="text" placeholder="city code"/>
<input type="text" placeholder="author name"/>
<textarea value={this.props.storyTextValue} onChange={this.handleStoryText} rows="2" cols="25" />
<input type="submit" value="Submit"/>
</form>
</div>
);
} }
const mapStateToProps = state => {
return {
articleIdValue: state.articleIdValue.articleIdValue,
storyTextValue: state.storyTextValue.storyTextValue
}; };
const mapDispatchToProps = dispatch => {
return {
articleIdValueRedux: (value) => dispatch({type: actionType.ARTICLE_ID_VALUE, value}),
storyTextValueRedux: (value) => dispatch({type: actionType.STORY_VALUE, value})
}; };
export default connect(mapStateToProps,
mapDispatchToProps)(CreateArticle)
;
I have 2 files
App.js
import React, { Component } from 'react';
import './App.css';
import ToDo from './components/ToDo.js';
class App extends Component {
constructor(props) {
super(props);
this.state = {
todos: [],
newTodoDescription: ''
};
this.deleteTodo = this.deleteTodo.bind(this);
}
handleChange(e) {
this.setState({ newTodoDescription: e.target.value })
}
handleSubmit(e) {
e.preventDefault();
if (!this.state.newTodoDescription) { return }
const newTodo = { id: this.state.todos.id, description: this.state.newTodoDescription, isCompleted: false };
this.setState({ todos: [...this.state.todos, newTodo], newTodoDescription: '' });
}
toggleComplete(index) {
const todos = this.state.todos.slice();
const todo = todos[index];
todo.isCompleted = todo.isCompleted ? false : true;
this.setState({ todos: todos });
}
deleteTodo(id) {
const remainingToDos = this.state.todos.filter((todo, remainingToDos) => {
if(todo.id !== remainingToDos.id) return todo; });
this.setState({ todos: remainingToDos });
}
render() {
return (
<div className="App">
<h1>Add a ToDo!</h1>
<form onSubmit={ (e) => this.handleSubmit(e)}>
<input type="text"
value={ this.state.newTodoDescription }
onChange={ (e) => this.handleChange(e) }
/>
<input type="submit" value="Add Todo" />
</form>
<ul>
{ this.state.todos.map( (todo) =>
<ToDo key={ todo.id }
description={ todo.description }
isCompleted={ todo.isCompleted }
toggleComplete={ () => this.toggleComplete(todo) }
onDelete={ this.deleteTodo }
/>
)}
</ul>
</div>
);
}
}
export default App;
ToDo.js
import React, { Component } from 'react';
class ToDo extends Component {
render() {
return (
<li>
<input type="checkbox" checked={ this.props.isCompleted } onChange={ this.props.toggleComplete } />
<span>{ this.props.description } {''}</span>
<button onClick={() => this.props.onDelete(this.props.id)}>Remove Todo</button>
</li>
);
}
}
export default ToDo;
What I am trying to accomplish: Add many todos to the list. Click on the remove todo button. ONLY the todo that is selected will be removed.
I am VERY new to react and cannot figure this out. In my deleteToDo method I am trying to filter out the todos and only keep the todos that are current. I am unclear if I am using .filter properly or not.
Problem is that filter() method should return a condition, not value:
deleteTodo(id) {
const remainingToDos = this.state.todos.filter((todo, remainingToDos) => {
return (todo.id !== remainingToDos.id)
});
this.setState({ todos: remainingToDos });
}
I am fairly sure you have simply over-complicated your code. You are not using the parameter id that you have passed into your method at all. Your deleteTodo method could simply be:
deleteTodo = (id) => {
var remainingToDos = this.state.todos.filter((todo) => {
return todo.id === id
});
this.setState({ todos: remainingToDos })
}