I'm building a simple blog app, and have a working editor to publish posts. I am now trying to implement one for the user to update a previous post. The editor state gets updated to the correct content, but it is not editable. I have also tried using a different editor and had the same problem. Does anyone have an idea as to what can be causing this? My best guess is that it has to do with how the data is loaded into the editor.
This is the function to update editorstate passed into the component using useContext
const updateTextDescription = (state) => {
setEditorState(state);
};
This is the update page.
import React from "react";
import classes from "./UpdateModal.module.css";
import './UpdateModal.css';
import { useBlogCtx } from "../../context/BlogContext";
import { EditorState, ContentState, convertFromHTML } from "draft-js";
import "../../../node_modules/draft-js/dist/Draft.css";
import { Editor } from "react-draft-wysiwyg";
import "react-draft-wysiwyg/dist/react-draft-wysiwyg.css";
import './UpdateModal.css';
import 'draft-js/dist/Draft.css';
function UpdateModal(props) {
const {
updatePost,
updateModalHandler,
setTitle,
setImage,
setPost,
currentId,
title,
image,
post,
updateTextDescription,
} = useBlogCtx();
const blocksFromHTML = convertFromHTML(post);
const content = ContentState.createFromBlockArray(
blocksFromHTML.contentBlocks,
blocksFromHTML.entityMap
);
const editorDataState = EditorState.createWithContent(content);
return (
<div
onClick={() => {
updateModalHandler();
}}
className={classes.backdrop}
>
<form onClick={(e) => e.stopPropagation()} className={classes.form}>
<label htmlFor="title">Title</label>
<input
id="title"
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
<label htmlFor="image">Image(URL)</label>
<input
id="image"
type="text"
value={image}
onChange={(e) => setImage(e.target.value)}
/>
<label htmlFor="post">Post</label>
<Editor
editorState={editorDataState}
onEditorStateChange={updateTextDescription}
wrapperClassName="rich-editor demo-wrapper"
editorClassName="demo-editor"
/>
<div className={classes.buttons}>
<button className={classes.cancel} onClick={updateModalHandler}>
Cancel
</button>
<button
className={classes.update}
onClick={() => {
updatePost(currentId);
}}
>
Update
</button>
</div>
</form>
</div>
);
}
export default UpdateModal;
This is what the editor looks like. editor
Content correctly gets set to editor state, just not editable.
Related
let me explain my situation.
I am building a MERN project to my portfolio and I am trying to make a button toggle between the name of an item and a inputfield. So when the user click the pen (edit), it will add a class with the displain:none; in the div with the text coming from the MongoDB data base to hide it and will remove it from the div with the input. I could manage to do it. BUT since the amount of items can inscrease, clicking in one of them cause the toggle in all of them.
It was ok until I send some useState as props to the component.
This is my code from the App.jsx
import React, {useState, useEffect} from "react";
import Axios from "axios";
import "./App.css";
import ListItem from "./components/ListItem";
function App() {
//here are the use states
const [foodName, setFoodName] = useState("");
const [days, setDays] = useState(0);
const [newFoodName, setNewFoodName] = useState("");
const [foodList, setFoodList] = useState([]);
//here is just the compunication with the DB of a form that I have above those components
useEffect(() => {
Axios.get("http://localhost:3001/read").then((response) => {
setFoodList(response.data);
});
}, []);
const addToList = () => {
Axios.post("http://localhost:3001/insert", {
foodName: foodName,
days: days,
});
};
const updateFood = (id) => {
Axios.put("http://localhost:3001/update", {
id: id,
newFoodName: newFoodName,
});
};
return (
<div className="App">
//Here it starts the app with the form and everything
<h1>CRUD app with MERN</h1>
<div className="container">
<h3 className="container__title">Favorite Food Database</h3>
<label>Food name:</label>
<input
type="text"
onChange={(event) => {
setFoodName(event.target.value);
}}
/>
<label>Days since you ate it:</label>
<input
type="number"
onChange={(event) => {
setDays(event.target.value);
}}
/>
<button onClick={addToList}>Add to list</button>
</div>
//Here the form finishes and now it starts the components I showed in the images.
<div className="listContainer">
<hr />
<h3 className="listContainer__title">Food List</h3>
{foodList.map((val, key) => {
return (
//This is the component and its props
<ListItem
val={val}
key={key}
functionUpdateFood={updateFood(val._id)}
newFoodName={newFoodName}
setNewFoodName={setNewFoodName}
/>
);
})}
</div>
</div>
);
}
export default App;
Now the component code:
import React from "react";
//Material UI Icon imports
import CancelIcon from "#mui/icons-material/Cancel";
import EditIcon from "#mui/icons-material/Edit";
//import CheckIcon from "#mui/icons-material/Check";
import CheckCircleIcon from "#mui/icons-material/CheckCircle";
//App starts here, I destructured the props
function ListItem({val, key, functionUpdateFood, newFoodName, setNewFoodName}) {
//const [foodList, setFoodList] = useState([]);
//Here I have the handleToggle function that will be used ahead.
const handleToggle = () => {
setNewFoodName(!newFoodName);
};
return (
<div
className="foodList__item"
key={key}>
<div className="foodList__item-group">
<h3
//As you can see, I toggle the classes with this conditional statement
//I use the same classes for all items I want to toggle with one click
//Here it will toggle the Food Name
className={
newFoodName
? "foodList__item-newName-delete"
: "foodList__name"
}>
{val.foodName}
</h3>
<div
className={
newFoodName
? "foodList__item-newName-group"
: "foodList__item-newName-delete"
}>
//Here is the input that will replace the FoodName
<input
type="text"
placeholder="The new food name..."
className="foodList__item-newName"
onChange={(event) => {
setNewFoodName(event.target.value);
}}
/>
//Here it will confirm the update and toggle back
//Didn't implement this yet
<div className="foodList__icons-confirm-group">
<CheckCircleIcon
className="foodList__icons-confirm"
onClick={functionUpdateFood}
/>
<small>Update?</small>
</div>
</div>
</div>
//here it will also desappear on the same toggle
<p
className={
newFoodName
? "foodList__item-newName-delete"
: "foodList__day"
}>
{val.daysSinceIAte} day(s) ago
</p>
<div
className={
newFoodName
? "foodList__item-newName-delete"
: "foodList__icons"
}>
//Here it will update, and it's the button that toggles
<EditIcon
className="foodList__icons-edit"
onClick={handleToggle}
/>
<CancelIcon className="foodList__icons-delete" />
</div>
</div>
);
}
export default ListItem;
I saw a solution that used different id's for each component. But this is dynamic, so if I have 1000 items on the data base, it would display all of them, so I can't add all this id's.
I am sorry for the very long explanation. It seems simple, but since I am starting, I spent the day on it + searched and tested several ways.
:|
I´m using a npm of inputs plus react hooks but when i submit the data i get undefined values in my console. I tried using the default input tags and works fine, the data i send shows perfectly. Any suggestions? is it possible to work with this NPM and react hook form or should i use the default data (Something that i don´t really like to do)
import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';
import Nav from "./Navbar";
import Footer from "./Footer";
import { FormField } from 'react-form-input-fields';
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { useForm } from "react-hook-form";
import { faEye,faEyeSlash } from '#fortawesome/free-solid-svg-icons';
import 'react-form-input-fields/dist/index.css';
function Login() {
const {register, handleSubmit } = useForm();
const eye = <FontAwesomeIcon icon={faEye} />
const closeEye = <FontAwesomeIcon icon={faEyeSlash} />
const [passwordShown, setPasswordShown] = useState(false);
let [email, setEmail] = useState("");
let [password, setPassword] = useState("");
const togglePasswordVisiblity = () => {
setPasswordShown(passwordShown ? false : true);
};
const onSubmit = (data) => {
console.log(data)
}
return (
<div className="page-container">
<div className="content-wrap">
<Nav />
<div className="div-login-form">
<h1 className="title">Login</h1>
<form className="login-form" onSubmit={handleSubmit(onSubmit)}>
<FormField
type="email"
standard="labeleffect"
value={email}
keys={'email'}
name="email"
effect={'effect_1'}
handleOnChange={(value) => setEmail(value)}
{...register("email")}
placeholder={'Enter Email'} />
<div className="input-password">
<div className="icon-eye">
<i onClick={togglePasswordVisiblity} className="icon"> {passwordShown ? eye : closeEye} </i>
</div>
<FormField
type={passwordShown ? "text" : "password"}
standard="labeleffect"
value={password}
keys={'password'}
name="password"
effect={'effect_1'}
handleOnChange={(value) => setPassword(value)}
{...register("password")}
placeholder={'Enter Password'} />
</div>
<button className="button-shop" type="submit">
Log in
</button>
</form>
</div>
</div>
<Footer />
</div>
);
}
export default Login;
You're not passing anything into your onSubmit function.
Rewrite it to something like this with your current setup:
onSubmit={() =>
handleSubmit(onSubmit({ email: email, password: password }))
}
Here's a minimal sandbox example
Aside
By the way, NPM is a package manager, not a component or element provider like you're referring to it by. Check out the useState docs for a good intro to states and React development.
I'm still a beginner with ReactJS and I need to mirror my switch button in my application.
My switch button change the language from my application, and I have the same button in the header and footer of my pages.
When I change the language from the site, the another button doesn't change at the same time, for example, if I click in my header component to change the language, the button in my footer stay the same way.
I put this example into codesandbox.io
Can you tell me how do I fix the buttons?
import "./styles.scss";
import { I18nProvider } from "./providers/i18n";
import ToggleLanguage from "./components/ToggleLanguage/ToggleLanguage.js";
export default function App() {
return (
<I18nProvider>
<div className="App">
<h3>Example Header</h3>
<ToggleLanguage />
<div style={{ margin: "20px 0" }} />
<h3>Example Footer</h3>
<ToggleLanguage />
</div>
</I18nProvider>
);
}
import React from "react";
import "./ToggleLanguage.scss";
// providers
import { AppContext } from "../../providers/app";
import { saveToStorage } from "../../utils/localStorage";
const ToggleLanguage = () => {
const { state, dispatch } = React.useContext(AppContext);
const onToggleSiteLang = (siteLang) => () => {
dispatch({ type: "setLang", siteLang });
saveToStorage("siteLang", siteLang);
};
return (
<div className="toggle-language">
<label className="switch">
<input
onChange={() => onToggleSiteLang(state.siteLang)}
className="switch-checkbox"
type="checkbox"
/>
<div className="switch-button" />
<div className="switch-labels">
<span>PT</span>
<span>EN</span>
</div>
</label>
</div>
);
};
export default ToggleLanguage;
Thank you very much in advance for any help/tip.
Just control your checkbox with your state. checked={state.siteLang === 'en'}
import React from "react";
import "./ToggleLanguage.scss";
// providers
import { AppContext } from "../../providers/app";
import { saveToStorage } from "../../utils/localStorage";
const ToggleLanguage = () => {
const { state, dispatch } = React.useContext(AppContext);
const onToggleSiteLang = (siteLang) => () => {
dispatch({ type: "setLang", siteLang });
saveToStorage("siteLang", siteLang);
};
return (
<div className="toggle-language">
<label className="switch">
<input
onChange={() => onToggleSiteLang(state.siteLang)}
className="switch-checkbox"
type="checkbox"
checked={state.siteLang === 'en'}
/>
<div className="switch-button" />
<div className="switch-labels">
<span>PT</span>
<span>EN</span>
</div>
</label>
</div>
);
};
export default ToggleLanguage;
Let me know if this works for you
I am trying to build a custom Draft.js ComponentBlock to accept an image URL in order to display an image within the Editor component. I tried using an tag and a "useState" hook, updated via a function onChange, that could store the input tag's value until the user was done typing/pasting the URL. However, the input tag was unusable because the text written in the input field would not appear in the input field, but would be stored in the EditorState until about 5 characters into typing, and pasting wouldn't display at all. I did some looking and found this:
https://draftjs.org/docs/advanced-topics-block-components/
which suggests using an "EditorBlock" component instead. However, while on the screen the component displays text just fine, I'm struggling to find a way to retrieve the data that is entered into the EditorBlock component. See in the code below.
Thank you!
import React, {useState} from 'react';
import { EditorBlock } from 'draft-js';
export function ImageButton(props){
const [ imageURL, setImageURL ] = useState("");
const [ insertURL, setInsertURL ] = useState("");
console.log(insertURL);
function handleSelect(){
setImageURL(insertURL)
}
if(imageURL == ""){
return(
<div>
<p>Insert URL:</p>
{/* <input
value={insertURL}
onChange={(evt) => {handleInputChange(evt, setInsertURL)}}
/> */}
<div>
<EditorBlock {...props} onChange={(env) => setInsertURL(env.target.value)}/>
</div>
<button onClick={handleSelect}>Select</button>
</div>
)
}
else{
return(
<div>
<img src={imageURL} />
</div>
)
}
}
I solved it, here's the new code
import React, {useState} from 'react';
import { EditorBlock } from 'draft-js';
export function ImageButton(props){
const [ imageURL, setImageURL ] = useState("");
//const [ insertURL, setInsertURL ] = useState("");
const propsData = Object.entries(props);
console.log(propsData[1][1]._map._root.entries[1][1])
function handleSelect(data){
setImageURL(data)
}
if(imageURL == ""){
return(
<div>
<p>Insert URL:</p>
<div>
<EditorBlock {...props}/>
</div>
<button onClick={() => handleSelect(propsData[1][1]._map._root.entries[1][1])}>Select</button>
</div>
)
}
else{
return(
<div>
<img src={imageURL} />
</div>
)
}
}
Problem 1: Content not showing after the last Else statement in index.jsx file for the form, fixing it would be deleting the last Else statement but this leads to another problem.
Problem 2: After pressing the Send Button in the mail form, the page refreshes but nothing shows in either database nor the message that Mail Successfully sent.
i hope someone can help me. No Error Messages in console
Here's all the files:
index.jsx(mail):
import React, { Component } from "react";
import * as mailActions from "../../store/actions/mail";
import { connect } from "react-redux";
import Loader from "../Loader";
import TextField from "#material-ui/core/TextField";
import SendIcon from "#material-ui/icons/Send";
import AddIcon from "#material-ui/icons/Add";
import "./style.css";
class CreateMail extends Component {
state = {
createMail: false,
};
createMailHandler = () => {
this.setState((prevState) => {
return {
createMail: !prevState.createMail,
};
});
};
render() {
let content;
if (this.props.loading) {
content = <Loader />;
} else if (this.props.error) {
content = (
<div>
{this.props.errorMessages.map((error, i) => (
<p key={i}>{error}</p>
))}
</div>
);
} else if (this.props.mailSent) {
content = <p>Mail sent successfully!</p>;
} else {
content = (
<form className="createMailForm">
<div className="formControl">
<TextField
id="receiver"
label="Receiver"
type="text"
name="receiver"
placeholder="Username of receiver"
/>
</div>
<div className="formControl">
<TextField
label="Subject"
type="text"
name="subject"
placeholder="Subject"
id="subject"
/>
</div>
<div className="formControl">
<TextField id="body" label="Body of the Mail" multiline rows={6} />
</div>
<button className="createMailSendBtn">
<SendIcon />
</button>
</form>
);
}
return (
<>
<div className="createMailContainer" onClick={this.createMailHandler}>
<button className="createMailCreateBtn">
<AddIcon
className={`${
this.state.createMail ? "createMailCreateBtn--close" : ""
}`}
/>
</button>
</div>
<div
className={`createMailFormContainer ${
this.state.createMail
? "formContainer--visible"
: "formContainer--hidden"
}`}
>
{content}
</div>
</>
);
}
}
const mapStateToProps = (state) => {
return {
...state.mail,
};
};
const mapDispatchToProps = (dispatch) => {
return {
mailInit: (mailObj) => dispatch(mailActions.mailInit(mailObj)),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(CreateMail);
Your Problem 2 is because of this:
<form className="createMailForm">
you have to pass a submit handler method here and that method must hold the form submit like:
<form className="createMailForm" onSubmit={this.submitHandler}>
and
submitHandler = (e) => {
e.preventDefault();
// Now perform your action here like make api call or call some action etc
}