DraftJS createWithContent returns undefined reading 'getBlockMap' - reactjs

I am using draftjs to update data from a server call. I am using MongoDB as the database and graphql as the query language. I can convert the data from the API using convertFromRaw(JSON.parse(data)) . I then set it to state, but when I attempt to createWithContent() i get an error Cannot read properties of undefined (reading 'getBlockMap'). My code is below. Been working on this for two days.
const GET_ISP_ENTRY = gql`
query IspListEntry($ispListEntryId: ID!) {
ispListEntry(id: $ispListEntryId) {
_id
displayName
contactFirstName
contactLastName
contactTitle
lastUpdated
onlineService
onlineAttn
address
city
state
zipCode
country
phoneNumber
extension
mobileNumber
faxNumber
email
website
referTo
notes
previous
}
}
`;
const UPDATE_ISP_ENTRY = gql`
mutation UpdateISPEntry($ispListEntryUpdateId: ID!, $input: UpdateISPEntry) {
ispListEntryUpdate(id: $ispListEntryUpdateId, input: $input) {
displayName
}
}
`;
const UpdateISPEntry = () => {
const ispEntryFields = {
displayName: '',
lastUpdated: Date(),
onlineService: '',
contactTitle: '',
contactFirstName: '',
contactLastName: '',
onlineAttn: '',
address: '',
city: '',
state: '',
zipCode: '',
country: '',
phoneNumber: '',
extension: '',
mobileNumber: '',
faxNumber: '',
email: '',
website: '',
referTo: '',
notes: '',
previous: ''
};
const [rawNotesFromDB, setRawNotesFromDB] = useState();
const [urlId, setUrlId] = useState('');
const [getIsp, { data, loading, error }] = useLazyQuery(GET_ISP_ENTRY, {
variables: {
ispListEntryId: urlId
},
onCompleted: () => {
loading
? console.log('Loading....')
: setEditorNotesState(
convertFromRaw(JSON.parse(data.ispListEntry.notes))
);
},
onError: () => {
toast.error(error);
}
});
const [editorNotesState, setEditorNotesState] = useState(() =>
EditorState.createWithContent().getCurrentContent()
);
console.log(editorNotesState);
const [formValues, setFormValues] = useState();
const [previousValue, setPreviousValue] = useState();
const [editorPreviousState, setEditorPreviousState] = useState();
const [
submitValues,
{ data: successful, loading: successLoading, error: loadingError }
] = useMutation(UPDATE_ISP_ENTRY, {
onError: () => {
toast.error(`There was an error ${loadingError}`);
}
});
const params = useLocation();
const path = params.pathname;
const pathSplit = path.split('/')[2];
useEffect(() => {
getIsp();
setFormValues(data && data.ispListEntry);
setUrlId(pathSplit);
}, [data, getIsp, pathSplit, formValues]);
// const convertedState = convertFromRaw(
// JSON.parse(data && data.ispListEntry.notes)
// );
// console.log(convertedState);
// const raw = () => {
// !formValues
// ? console.log('DAMN')
// : setRawNotes(convertFromRaw(JSON.parse(formValues.notes)));
// };
const handleSubmit = () => {};
return (
<Fragment>
<div className='container p-4 parent-container'>
<ISPFormHeader />
<ISPFormHeaderPagename children='Update ISP Entry' />
<ISPForm
initialValues={data && data.ispListEntry}
enableReinitialize={true}
onSubmit={handleSubmit}
/>
<div className='editor-fields'>
<div className='rt-editor'>
<header className='rt-editor-header'>Notes</header>
<EditorComponent
id='notes'
name='notes'
type='text'
editorState={editorNotesState}
onEditorStateChange={setEditorNotesState}
/>
</div>
<div className='rt-editor'>
<header className='rt-editor-header'>Previous</header>
<EditorComponent
name='previous'
id='previous'
type='text'
EditorState={editorPreviousState}
// onEditorStateChange={handleEditorPreviousChange}
/>
</div>
</div>
</div>
</Fragment>
);
};
export default UpdateISPEntry;

Related

re-render not behaving properly

So I deployed the app to demonstrate the problem
https://ecstatic-mayer-aaa530.netlify.app/
When you 'refresh' the page new data is fetched and user name is displayed (along with active state)
BUT when I click random user button, I don't get the same behavior even though I refetch the data. I want to get the same behavior as page refresh. (Name is displayed with the active state)
To better observe the problem you can : Hover on other fields, click on random user btn and then re-enter with your mouse on name (profile icon). It is not updating as you'll see
import './App.css';
import ProfileCard from './components/ProfileCard';
import React, { useEffect, useState } from 'react';
function App() {
const [userData, setUserData] = useState({});
const [triggerFetch, setTriggerFetch] = useState(false);
const fetchUserData = async () => {
const response = await fetch('https://randomuser.me/api/');
const data = await response.json();
setUserData(setFields(data.results[0]));
};
const setFields = (userData) => {
const fName = userData.name.first;
const lName = userData.name.last;
const fullName = fName + ' ' + lName;
return {
image: userData.picture.large,
name: fullName,
email: userData.email,
dob: userData.dob.date,
location: userData.location.street.name,
phone: userData.phone,
password: userData.login.password,
};
};
useEffect(() => {
fetchUserData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [triggerFetch]);
//this triggers fetch
const handleRandomUserClick = () => {
setTriggerFetch(!triggerFetch);
};
return (
<main>
<div className='grey-bg'></div>
<ProfileCard
userData={userData}
handleRandomUserClick={handleRandomUserClick}
/>
</main>
);
}
export default App;
import React from 'react';
import {
FaUserAlt,
FaPhoneAlt,
FaBirthdayCake,
FaLock,
FaLocationArrow,
FaEnvelope,
} from 'react-icons/fa';
const ProfileCard = ({ userData, handleRandomUserClick }) => {
const { image, name, email, dob, location, phone, password } = userData;
const [randomUserClicked, setrandomUserClicked] = React.useState(false);
const defaultFlag = {
name: 'active',
email: '',
dob: '',
location: '',
phone: '',
password: '',
};
const [flag, setFlag] = React.useState(defaultFlag);
const [title, setTitle] = React.useState('My Name Is');
const [value, setValue] = React.useState(name);
const handleValue = (e) => {
if (e.target.dataset.label === 'name') {
setTitle('My Name is');
setValue(name);
setFlag(defaultFlag);
} else if (e.target.dataset.label === 'email') {
setTitle('My Email is');
setValue(email);
setFlag({
name: '',
email: 'active',
dob: '',
location: '',
phone: '',
password: '',
});
} else if (e.target.dataset.label === 'dob') {
setTitle('My Birthday is');
setValue(new Date(dob).toLocaleDateString());
setFlag({
name: '',
email: '',
dob: 'active',
location: '',
phone: '',
password: '',
});
} else if (e.target.dataset.label === 'location') {
setTitle('I live in');
setValue(location);
setFlag({
name: '',
email: '',
dob: '',
location: 'active',
phone: '',
password: '',
});
} else if (e.target.dataset.label === 'phone') {
setTitle('My phone number is');
setValue(phone);
setFlag({
name: '',
email: '',
dob: '',
location: '',
phone: 'active',
password: '',
});
} else {
setTitle('My password');
setValue(password);
setFlag({
name: '',
email: '',
dob: '',
location: '',
phone: '',
password: 'active',
});
}
if (e.target.dataset.label === 'random') {
handleRandomUserClick();
setrandomUserClicked(!randomUserClicked);
}
};
React.useEffect(() => {
setTitle('My Name is');
setValue(name);
setFlag(defaultFlag);
}, [randomUserClicked]);
return (
<article className='profile-card'>
<img src={image} alt={name} />
<div className='user-details'>
<p className='user-title'>{title}</p>
<h3 className='user-value'>{value || name}</h3>
</div>
<ul className='card-list'>
<li className={flag.name} onMouseEnter={handleValue} data-label='name'>
<FaUserAlt className='icon' />
</li>
<li
data-label='email'
onMouseEnter={handleValue}
className={flag.email}
>
<FaEnvelope className='icon' />
</li>
<li data-label='dob' onMouseEnter={handleValue} className={flag.dob}>
<FaBirthdayCake className='icon' />
</li>
<li
data-label='location'
onMouseEnter={handleValue}
className={flag.location}
>
<FaLocationArrow className='icon' />
</li>
<li
data-label='phone'
onMouseEnter={handleValue}
className={flag.phone}
>
<FaPhoneAlt className='icon' />
</li>
<li
data-label='password'
onMouseEnter={handleValue}
className={flag.password}
>
<FaLock className='icon' />
</li>
</ul>
{/* Also handling this click within handleValue function */}
<button className='btn' onClick={handleValue} data-label='random'>
Random User
</button>
</article>
);
};
export default ProfileCard;
The problem happens, because in ProfileCard component, userData is not listed under useEffects's dependencies array.
By the way, your component design is very prone to bugs. In React functional components should be a simple functions that receive some data (props) and return JSX. Hooks should be used only when you actually have to use them. In your app, you're creating a complicated network of effects, state updates and re-renders, which makes it hard to maintain.
Let's write all hooks you actually need:
one state for the data fetched from an api
one state for keeping which section is currently active (name, email, etc.)
one effect for fetching the data
And that's it! Everything else can be just passed through props.

How can I disable/gray out dropdown selection checkboxes for two records available in setSubmittedNominees state?

How can I disable/gray out dropdown selection checkboxes for two records available in a state setSubmittedNominees in react hooks ? I tried to pass into submittedNominees into selectedValues and disablePreSelectedValues(true) but it doesn't work that way can someone please advise on this ?
codesandbox link:
https://codesandbox.io/s/magical-haslett-s0oeh?file=/src/App.js
import React, { useRef, useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import { useForm } from "react-hook-form";
import Axios from "axios";
import { Link, useHistory } from "react-router-dom";
import Multiselect from "multiselect-react-dropdown";
const options = [
{ id: 1, name: "Ath", email: "ath.best#test1.com", access: null },
{ id: 2, name: "Arnolds", email: "arnold#test1.com", access: null },
{ id: 3, name: "Alter", email: "alloop#test1.com", access: null },
{ id: 4, name: "Brandan", email: "brandan#test1.com", access: null },
{ id: 5, name: "Ron", email: "ron#test1.com", access: null },
{ id: 6, name: "Rads", email: "rad#test1.com", access: null },
{ id: 7, name: "Sam", email: "sam#y.com", access: null }
];
const submitted = [
{ id: 4, name: "Brandan", email: "brandan#test1.com", access: null },
{ id: 5, name: "Ron", email: "ron#test1.com", access: null }
];
const Selection= () => {
const [option, setOption] = useState([]);
const [selectedOption, setSelectedOption] = useState([]);
const [nomRegister, setNomRegister] = useState([{}]);
const [helperText, setHelperText] = useState("");
const [userEmail, setUserEmail] = useState("");
const [submittedNominees, setSubmittedNominees] = useState([{}]);
const {
register,
handleSubmit,
watch,
formState: { errors },
reset
} = useForm();
const maxOptions = 3;
const history = useHistory();
useEffect(() => {
const userEmail = localStorage.getItem("loginEmail");
setUserEmail(userEmail);
});
useEffect(() => {
const fetchData = async () => {
try {
const res = await Axios.get(
"http://localhost:8000/service/nomineeslist"
);
//const data1 = res.data;
setOption(options);
console.log("Get the list of nominees :" + JSON.stringify(res.data));
} catch (e) {
console.log(e);
}
};
fetchData();
}, []);
useEffect(() => {
const fetchData = async () => {
try {
// const res = await Axios.get(
// "http://localhost:8000/service/submittednominees"
// );
setSubmittedNominees(submitted);
} catch (e) {
console.log(e);
}
};
fetchData();
}, []);
const handleTypeSelect = (e, i) => {
const copy = [...selectedOption];
copy.push(e[i]);
setSelectedOption(copy);
};
const sendNomination = () => {
console.log("What the Array holds: " + JSON.stringify(nomRegister));
const fetchData = async (nomRegister) => {
try {
const res = await Axios.post(
"http://localhost:8000/service/nominateperson",
{ userEmail },
nomRegister
);
if (res.data) {
console.log("Print data:" + res.data);
const successMessage = res.data.message;
setHelperText(successMessage);
setNomRegister(reset);
}
} catch (e) {
console.log(e);
setNomRegister(reset);
setHelperText(e.message);
}
};
fetchData();
};
options.forEach((option) => {
option.displayValue = option.name + "\t" + option.email;
submittedNominees.forEach((item) => {
let subEmail = item.email; // how can I pass those two email to selectedValues and make it disable ?
});
});
const handleChange = (e, i) => {
const { name, email, value } = e.target;
// immutating state (best practice)
const updateList = nomRegister.map((item) => {
return { ...item };
});
const select_Email = selectedOption.map((item) => {
return item.email;
});
//change the specific array case depends on the id //email:emailList[i],
updateList[i] = {
...updateList[i],
name: name,
email: select_Email[i],
reason: value
};
setNomRegister(updateList);
};
return (
<div className="App">
<h1>Nominate a person</h1>
<div className="nomineeSelectBox">
<div id="dialog2" className="triangle_down1" />
<div className="arrowdown">
<Multiselect
onSelect={(e) => handleTypeSelect(e, selectedOption.length)}
options={selectedOption.length + 1 === maxOptions ? [] : options}
displayValue="displayValue"
selectedValues={subEmail}
showCheckbox={true}
emptyRecordMsg={"Maximum nominees selected !"}
/>
</div>
</div>
<div className="nominationcount"></div>
<form onSubmit={handleSubmit(sendNomination)}>
<div className="nomineesSelectedList">
<h4>Selected Nominees</h4>
{selectedOption.map((x, i) => (
<div key={i}>
<div className="row eachrecord">
<div className="column">
<label className="nomlabel">
{x?.name} <b>>></b>
</label>
</div>
<input
required
type="textarea"
placeholder="Please provide reason for nomination.."
key={i}
id={i}
name={x?.name}
className="nomineechoosed"
onChange={(e) => handleChange(e, i)}
/>
</div>
</div>
))}
<div className="row">
<div className="buttongroup">
<input id="Submit" type="submit" value="Submit" />
<input id="Cancel" type="button" value="Cancel" />
</div>
</div>
</div>
</form>
<span className="nominationValidationText">{helperText}</span>
</div>
);
};
export default Selection;
the issue here is that you are showing your dropdown selection checkboxes by displayValue (displayValue="displayValue") and your submitted array that you you will assign to submittedNominees (setSubmittedNominees(submitted)) does not contain displayValue:
const submitted = [
{ id: 4, name: "Brandan", email: "brandan#test1.com", access: null },
{ id: 5, name: "Ron", email: "ron#test1.com", access: null }
];
Solution::
Make your submitted for example like this:
const submitted = [
{
id: 4,
name: "Brandan",
email: "brandan#test1.com",
access: null,
displayValue: "Brandan brandan#test1.com"
},
{
id: 5,
name: "Ron",
email: "ron#test1.com",
access: null,
displayValue: "Ron ron#test1.com"
}
];
Add selectedValues={submittedNominees} to your Multiselect to find out which values you will disable:
<Multiselect
onSelect={(e) => handleTypeSelect(e, selectedOption.length)}
options={selectedOption.length + 1 === maxOptions ? [] : options}
displayValue="displayValue"
disablePreSelectedValues={true}
selectedValues={submittedNominees}
showCheckbox={true}
emptyRecordMsg={"Maximum nominees selected !"}
/>
Codesandbox link

React Hooks - set state to initial state

I am using React useState to create an object in state.
This is updated to an object of data after a successful API call.
I have a form that can change this state, but I also have a cancel button.
How can i restore this state to its initial values (after API call) when cancel is clicked?
Should i create another state variable and store initial state there and then update my state based on that?
const [basePosition, setBasePosition] = useState({});
const [position, setPosition] = useState({
id: '',
title: '',
description: '',
authoredBy: '',
createdDate: '',
lastUpdatedBy: '',
lastUpdateDate: '',
sliderResponses: [],
tileResponses: [{}],
template: {}
});```
const initialState = {
id: '',
title: '',
};
const Test = () => {
const [position, setPosition] = useState(initialState);
return <>
...form
<button onClick={() => setPosition(initialState)}>Reset</button>
</>;
};
Don't create another state variable just to store initial state as it will cause another re render instead when your component is mounted then intialize your initial state object:
let initialState = null;
React.useEffect(() => {
initialState = position;
},[])
When you want to reset to initial state just use:
setPosition(initialState);
You need not to create another State. Just declare an initial state which will not be changed and assign it to the Position state when it is needed to be reset. EX:
import React,{useState} from 'react'
const YourComponent = () =>{
const initialState = {
id: '',
title: '',
description: '',
authoredBy: '',
createdDate: '',
lastUpdatedBy: '',
lastUpdateDate: '',
sliderResponses: [],
tileResponses: [{}],
template: {}
}
const [basePosition, setBasePosition] = useState({});
const [position, setPosition] = useState(initialState);
const resetState = () =>{
setPosition(initialState)
}
}
Answer to your question if you should store initial value is Yes.
That would be the easiest way to maintain your code. So put your initial value in a constant:
const INITIAL_VALUES = {
id: '',
title: '',
description: '',
authoredBy: '',
createdDate: '',
lastUpdatedBy: '',
lastUpdateDate: '',
sliderResponses: [],
tileResponses: [{}],
template: {}
}
Than every time you want to use that initial object, just spread it and all is good (spread to lose reference to constant):
const [basePosition, setBasePosition] = useState({});
const [position, setPosition] = useState({...INITIAL_VALUES});
And later when you reset:
setPosition({...INITIAL_VALUES})
import React, { useState } from 'react'
// counter
function Example3() {
const [initial, setIncrement] = useState(0)
const increment = () => {
setIncrement(initial + 1)
}
const dincrement = () => {
setIncrement(initial - 1)
}
const reset = () => {
setIncrement(0)
}
return (
<div>
<p>{initial}</p>
<button onClick={increment} >+</button>
<button onClick={dincrement} >-</button>
<button onClick={reset}>reset</button>
</div>
)
}
export default Example3;

How to make field validation?

How to make field validation?
I have an object with fields from which I generate a form, and when submitting, I need to check each field so that it is not empty, I do this, but it doesn’t work
My form:
const [volunteerData, setVolunteerData] = useState({
fullName: {
value: '',
type: "text",
placeholder: "Name",
label: "Name"
},
phone: {
value: '',
type: "text",
placeholder: "Phone number",
label: "Phone number",
mask: "+7(999) 999 99 99"
}
)}
Render form:
const onHandleRenderForm = () => {
return Object.keys(volunteerData).map((items, idx) => {
const control = volunteerData[items];
return (
<div key={idx} className="content-data-box">
<label>{control.label}</label>
<InputMask
type={control.type}
placeholder={control.placeholder}
mask={control.mask}
onChange={e => onHandleFormData(e, items)}
/>
</div>
)
})
};
onChange input:
const onHandleFormData = (e, items) => {
const before = {...volunteerData};
const after = {...before[items]}
after.value = e.target.value;
before[items] = after;
setVolunteerData(before);
};
onClick (submit button):
const onHandleErrorBoundary = () => {
Object.keys(volunteerData).map(items => {
const errorData = items.value === '';
console.log(errorData)
})
};
Change items.value === '' to volunteerData[items].value !== ""
const onHandleErrorBoundary = () => {
Object.keys(volunteerData).map(items => {
const errorData = volunteerData[items].value !== "";
return console.log(errorData);
});
};
you can check here codesandbox

React-Admin: How to send input values that have been auto filled from an API call?

I have an input 'A' that fetches address data from an API and auto fills inputs 'B' 'C' and 'D' based on that data, but after the inputs have been filled and I try to send that form to my backend, none of those auto filled inputs are sent, just the input 'A' is sent. Furthermore, if i manually edit any of the inputs (remove a char, add a space, change the value) the ones that I edited get sent to my backend.
I'm using a reducer to store the state. The inputs that I'm using are all just normal react-admin TextInput components.
Here's the code:
const AutoFill = () => {
const [searching, setSearching] = useState(false);
const [error, setError] = useState(false);
const [stateData, setStateData] = useReducer(
(state, newState) => ({ ...state, ...newState }),
{
cep: ' - ',
address: '',
number: '',
neighborhood: '',
city: '',
state: '',
}
);
const FormControl = (event) => {
const { name, value } = event.target;
setStateData({ [name]: value });
};
const SearchControl = (event) => {
const { name, value } = event.target;
setStateData({ [name]: value });
if (value && !value.includes('_')) {
setSearching(true);
setStateData({ state: '...' });
setStateData({ city: '...' });
setStateData({ neighborhood: '...' });
setStateData({ address: '...' });
cep(value.replace('-', '')).then(
(result) => {
setSearching(false);
setError(false);
setStateData({ state: result.state });
setStateData({ city: result.city });
setStateData({ neighborhood: result.neighborhood });
setStateData({ address: result.street });
},
() => {
setSearching(false);
setError(true);
setStateData({ state: '' });
setStateData({ city: '' });
setStateData({ neighborhood: '' });
setStateData({ address: '' });
}
);
}
};
return (
<>
<TextInput
source="cep"
error={error}
value={stateData.cep}
onChange={SearchControl}
/>
<TextInput
source="address"
disabled={searching}
value={stateData.address}
onChange={FormControl}
/>
<TextInput
source="number"
disabled={searching}
value={stateData.number}
onChange={FormControl}
/>
<TextInput
source="neighborhood"
disabled={searching}
value={stateData.neighborhood}
onChange={FormControl}
/>
<TextInput
source="state"
disabled={searching}
value={stateData.state}
onChange={FormControl}
/>
<TextInput
source="city"
disabled={searching}
value={stateData.city}
onChange={FormControl}
/>
</>
);
};
export const Create = (props) => {
return (
<Create {...props}>
<SimpleForm>
<NumberInput label="Value" source="price" />
<AutoFill />
<RichTextInput label="Description" source="description" />
</SimpleForm>
</Create>
);
};
You're going to need to use React Final Form's FormState and Form solutions. Will use snippets of my code for example.
1) Grab the form values
const formState = useFormState();
const form = useForm();
const {
asset_system_parent_id: majorSystem,
classification,
} = formState.values;
2) Setup useEffect that will observe changes to a form field:
useEffect(() => {
const setFluidEnd = async () => {
DO SOMETHING!!!!!
};
if ('Fluid End Maintenance' === classification) {
setFluidEnd();
}
}, [classification, form, notify]);
3) Use form.change (+ form.batch if you need to update multiple inputs)
useEffect(() => {
const setFluidEnd = async () => {
await requestGetList('asset-systems', 'id', 'ASC', 500, {
description: 'Fluid End',
relationship: 'parent',
})
.then(res => {
form.change('asset_system_parent_id', res.data[0].id);
})
.catch(error => {
notify(`System Assets not found`, 'warning');
});
};
if ('Fluid End Maintenance' === classification) {
setFluidEnd();
}
}, [classification, form, notify]);
You can read more about the api here: https://final-form.org/docs/final-form/types/FormApi
Please use this code.
-index.js file
import axios from "axios";
export const setInputValue = (data) => {
return axios.get(`https://www.example.com/profile`)
.then((response) => {
return response.data;
});
};
-component.js
return setInputValue(value).then(() => {
this.setState(() => ({
loading: false
}));
});
...
render(){
return (
...
<input type="text" onClick={e => this.onClick(e)} value={this.state.value}/>
..
)}
...
react-admin.php
...
public function setInputValue(value)
{
try {
$user->set(value);
return response()->json(["result" => "successfully!"]);
} catch (\Exception $e) {
return getErrorResponse($e);
}
}

Resources