I have a project that can add, edit or delete movies from a list. I'm having trouble with the edit part of the component, next to every movie I added a text input to edit that movie if wanted, but I don't know how to pass that value to the other component in order to change the old value for the new one. Here is what I got for now:
Movie component:
import React, {Component} from 'react';
class Movie extends Component{
constructor(props){
super(props);
this.state= {
inputValue: ''
};
}
updateInputValue(evt) {
this.setState({
inputValue: evt.target.value
});
}
render(){
return(
<div>
{this.props.text}
<button onClick={this.props.deleteMethod}>X</button>
<input value={this.props.newMovieName}
onChange={evt => this.updateInputValue(evt)}
/>
<button onClick={this.props.editMethod().bind(this.inputValue)}>edit</button>
</div>
);
}
}
export default Movie;
Add component:
import React,{Component} from 'react';
import Movie from './Movie.jsx';
class AddComponent extends Component {
constructor(props){
super(props);
this.state = {
movieText: '',
movies: [],
};
}
updateMovieText(movieText){
this.setState({movieText: movieText.target.value})
}
addMovie(){
if(this.state.movieText === ''){return}
let moviesArr = this.state.movies;
moviesArr.push(this.state.movieText);
this.setState({movieText: ''})
this.textInput.focus();
}
handleKeyPress = (event) => {//enables to add when pressing enter on keyboard
if(event.key === 'Enter'){
let moviesArr = this.state.movies;
moviesArr.push(this.state.movieText);
this.setState({movieText: ''})
}
}
deleteMovie(index) {
let movieArr = this.state.movies;
movieArr.splice(index,1);//remove the movie from array
this.setState({movies: movieArr})
}
editMovie(index,value){
let moviesArr = this.state.movies;
moviesArr[index] = value;
this.setState({movies:movieArr});
}
render(){
let movie = this.state.movies.map((val,key)=> {//prints on screen list of movies see line55
return (<Movie
key={key}
text={val}
deleteMethod={() => this.deleteMovie(key)}
editMethod={() => this.editMovie(key,this.inputValue)}
/>
);
});
return (
<div>
<input type="text"
ref={((input)=>{this.textInput = input;})}
className="textInput"
value={this.state.movieText}
onChange={movieText => this.updateMovieText(movieText)}
onKeyPress={this.handleKeyPress.bind(this)}
/>
<button onClick={this.addMovie.bind(this)}>Add</button>
{movie}
</div>
);
}
}
export default AddComponent;
I think I should use bind, but for now it isn't working
You need to bind the key in AddComponent so you can identify the movie being edited e.g.
let movie = this.state.movies.map((val,key)=> {
return (<Movie
key={key}
text={val}
deleteMethod={() => this.deleteMovie(key)}
editMethod={this.editMovie.bind(this, key)}
/>
);
Then in the Movie component you need to pass the latest input value on click, e.g.
<button onClick={() => this.props.editMethod(this.state.inputValue)}>edit</button>
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 a StoreDetails functional componenet in Gatsby JS, it is rendering products conditionally from a map in graphQL that match the state value using UseState hook. I have a class component drop down menu that currently manually is populated with sku'ids that would match the condition to display in the state and map conditional statement. I want the dropdown to change the state of the functional component so the right product shows when it is selected in the drop down. I was playing around with passing the state function as a prop, but I got rerender issues, quite stuck here.
The key is at this line {value.skuId === sku.skuId ? How do I change that based on the option dropdown when it's still inside the map
thanks ahead of time
Here is my code so far
import React, {useState} from 'react';
import Layout from "../components/layout"
import styled from 'styled-components';
import {loadStripe} from '#stripe/stripe-js';
// import Alert from '../components/Alert';
import { navigate } from "gatsby";
import Img from "gatsby-image"
const StoreHero = styled.section`
width:1280px;
margin:0 auto;
`
class Alert extends React.Component{
constructor(props){
super(props);
//console.log(props);
this.state = {test:"test",hello:"hello1",hash:props.hash}
}
render(){
const { test, hello, hash } = this.state
//console.log(hash);
return(
<div>{hash}</div>
)
}
}
class ShowItem extends React.Component {
constructor(props) {
super(props);
this.state = {value: 'coconut'};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
console.log(event.target.value);
// Right here is where I want to change the state of the value variable below so
// that the correct product is shown based on the dropdown sku selection
}
render() {
return (
<select value={this.state.value} onChange={this.handleChange}>
<option value="sku_HAD1kUsbV3GpgW">sku_HAD1kUsbV3GpgW</option>
<option value="sku_HACMgLjJBZFR7A">sku_HACMgLjJBZFR7A</option>
</select>
);
}
}
class Product extends React.Component{
constructor(props) {
super(props);
this.state = {
stripe: null
};
this.loadStripeLib = this.loadStripeLib.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
componentDidMount() {
this.loadStripeLib()
}
async loadStripeLib() {
try {
const stripe = await loadStripe('pk_test_random');
this.setState({ stripe });
} catch {
// do nothing
}
}
handleSubmit(sku, productId){
return event => {
event.preventDefault();
this.state.stripe.redirectToCheckout({
items: [{sku, quantity: 1}],
successUrl: `http://localhost:8000/store/${productId}#success`,
cancelUrl: `http://localhost:8000/store/${productId}#cancelled`,
}).then(function (result) {
// Display result.error.message to your customer
console.error(result);
});
}
}
render(){
const { id, currency, price, name, productId } = this.props
const priceFloat = (price / 100).toFixed(2)
const formattedPrice = Intl.NumberFormat('en-US', {
style: 'currency',
currency,
}).format(priceFloat)
return(
<form onSubmit={this.handleSubmit(id, productId)}>
<h2>
{name} ({formattedPrice})
</h2>
<button type="submit">Buy Now</button>
</form>
)
}
}
const StoreDetails = ({data, location}) =>{
const [value, setValue] = useState({
skuId: "sku_HAD1kUsbV3GpgW"
});
//console.log(value);
return(
<Layout>
<StoreHero>
<Alert test="test" hello="hello" hash={location.hash}/>
{/* <ShowItem props={data}/> */}
{data.allDatoCmsStore.edges.map(({ node: sku }) => (
<>
{value.skuId === sku.skuId ?
<>
<ShowItem setValue={setValue}/>
<Product
key={sku.id}
id={sku.skuId}
productId={sku.productId}
currency="cad"
price={sku.price}
name={sku.title}
/>
<Img fixed={sku.image.fixed}/>
</>
:
null
}
</>
))}
</StoreHero>
</Layout>
)
}
export default StoreDetails
export const query = graphql`
query StoreDeatailsQuery($slug: String!) {
allDatoCmsStore(filter: {productId: {eq: $slug}}) {
edges {
node {
price
productId
skuId
title
id
image{
fixed{
...GatsbyDatoCmsFixed
}
}
}
}
}
allStripeSku {
edges {
node {
id
currency
price
attributes {
name
}
image
localFiles {
childImageSharp {
fixed(width: 125) {
...GatsbyImageSharpFixed
}
}
}
}
}
}
}
`
Looks like I was overcomplicating it, I have more to do, but changing it to this fixed it for me
const StoreDetails = ({data, location}) =>{
const [value, setValue] = useState({
skuId: "sku_HAD1kUsbV3GpgW"
});
const run = (event) =>{
console.log(event);
setValue({skuId:event})
}
return(
<Layout>
<StoreHero>
<Alert test="test" hello="hello" hash={location.hash}/>
{/* <ShowItem props={data}/> */}
{data.allDatoCmsStore.edges.map(({ node: sku }) => (
<>
{value.skuId === sku.skuId ?
<>
<select onChange={(event) => run(event.target.value)}>
<option value="sku_HAD1kUsbV3GpgW">sku_HAD1kUsbV3GpgW</option>
<option value="sku_HACMgLjJBZFR7A">sku_HACMgLjJBZFR7A</option>
</select>
{/* <ShowItem setValue={setValue}/> */}
<Product
key={sku.id}
id={sku.skuId}
productId={sku.productId}
currency="cad"
price={sku.price}
name={sku.title}
/>
<Img fixed={sku.image.fixed}/>
</>
:
null
}
</>
))}
</StoreHero>
</Layout>
)
}
React UI will change by reacting to state changes. if two child component need the same data (in this case the selected value). then the state of selected value should be on the parent component (in StoreDetails) so the Product will change when the value state on StoreDetails is changed by the function sent to the onChange as props.
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)}/>"
Im having issues with react todo list.
When submitted the list item appears fine.
I should then be able to delete this be clicking on the item,however nothing happens. As soon as i try to add another item the pages refreshes and all items are removed?
console log just shows
[object object]
App
import React, { Component } from 'react';
import './App.css';
import TodoItem from './TodoItem';
class App extends Component {
constructor(props) {
super(props);
this.state = {
items: []
};
this.addItems = this.addItems.bind(this);
this.deleteItem = this.deleteItem.bind(this);
}
addItems(e) {
if (this._inputElement !== '') {
let newItem = {
text: this._inputElement.value,
key: Date.now()
};
this.setState(prevState => {
return {
items: prevState.items.concat(newItem)
};
});
}
this._inputElement.value = '';
e.preventDefault();
}
deleteItem(key) {
console.log('key is' + key);
console.log('itesm as' + this.state.items);
var filteredItems = this.state.items.filter(function(item) {
return item.key !== key;
});
this.setState = {
items: filteredItems
};
}
render() {
return (
<div className="app">
<h2> things to do</h2>
<div className="form-inline">
<div className="header">
<form onSubmit={this.addItems}>
<input
ref={a => (this._inputElement = a)}
placeholder="enter task"
/>
<button type="submit">add</button>
</form>
</div>
</div>
<TodoItem entries={this.state.items} delete={this.deleteItem} />
</div>
);
}
}
export default App;
todoItem
import React, { Component } from 'react';
//search bar
class TodoItem extends Component {
constructor(props) {
super(props);
this.createTasks = this.createTasks.bind(this);
}
createTasks(item) {
return (
<li onClick={() => this.delete(item.key)} key={item.key}>
{item.text}
</li>
);
}
delete(key) {
console.log('key is ' + key);
this.props.delete(key);
}
render() {
let todoEntries = this.props.entries;
let listItems = todoEntries.map(this.createTasks);
return <ul className="theList">{listItems}</ul>;
}
}
export default TodoItem;
You are assigning to setState instead of using it as a method that it is
Change
this.setState = {
items: filteredItems
};
to
this.setState({
items: filteredItems
});
And that is also the reason it will reload the app, as you have overwritten the setState method you should be getting an error that setState is not a function and it would crash the app.
I have one Add/delete/edit component and a movie component. I want to edit a movie component inside Add, and then I want to display the new data in the place where old data displayed.
Here is my Add/edit/delete component:
import React, {Component} from 'react';
import Movie from './Movie.jsx';
class AddComponent extends Component {
constructor(props){
super(props);
this.state = {
movieText: '',
movies: [],
};
}
updateMovieText(movieText){
this.setState({movieText: movieText.target.value})
}
addMovie(){
if(this.state.movieText === ''){return}
let moviesArr = this.state.movies;
moviesArr.push(this.state.movieText);
this.setState({movieText: ''})
this.textInput.focus();
}
handleKeyPress = (event) => {//enables to add when pressing enter on keyboard
if(event.key === 'Enter'){
let moviesArr = this.state.movies;
moviesArr.push(this.state.movieText);
this.setState({movieText: ''})
}
}
deleteMovie(index) {
const movies = this.state.movies;
const newMovies = [
...movies.slice(0, index),
...movies.slice(index + 1)
];
this.setState({
movies: newMovies
});
// Notice movies !== new Movies
// and movies still contains all the previous values
}
editMovie(index,value){
const movies = this.state.movies;
const newMovies = movies.map((movie, i) => {//HERE is EDIT
if (i !== index) {
return movie;
}
return value;
});
this.setState({ movies: newMovies });
// Notice movies !== new Movies
// and movies still contains the previous values
}
render(){
let movie = this.state.movies.map((val,key)=> {//prints on screen list of movies see line55
return (<Movie
key={key}
text={val}
deleteMethod={() => this.deleteMovie(key)}
editMethod={this.editMovie.bind(this, key)}
/>
);
});
return (
<div>
<input type="text"
ref={((input)=>{this.textInput = input;})}
className="textInput"
value={this.state.movieText}
onChange={movieText => this.updateMovieText(movieText)}
onKeyPress={this.handleKeyPress.bind(this)}
/>
<button onClick={this.addMovie.bind(this)}>Add</button>
{movie}
</div>
);
}
}
export default AddComponent;
And this is my Movie component:
import React, {Component} from 'react';
class Movie extends Component{
constructor(props){
super(props);
this.state= {
inputValue: ''
};
}
updateInputValue(evt) {
this.setState({
inputValue: evt.target.value
});
}
render(){
return(
<div>
{this.props.text}
<button onClick={this.props.deleteMethod}>X</button>
<input value={this.props.newMovieName}
onChange={evt => this.updateInputValue(evt)}
/>
<button onClick={() => this.props.editMethod(this.inputValue)}>edit</button>
</div>
);
}
}
export default Movie;
With the code from above this.props.text disappears, leaving only the delete button, the edit text input and the edit button. If I console.log the value returned from edit function prints undefined
I guess you want to use this.state.inputValue instead of this.inputValue
You need just to pass the state to the Edit Method:
<button onClick={() =>
this.props.editMethod(this.state.inputValue)}>edit</button>