I am unable to save description as part of the component's state. I can only save the title. How do I save title and description to the database?
const BlogCreate = ({ history }) => {
const [blogCreate, setBlogCreate] = useState({
title: "",
description: ""
});
const [editorState, setEditorState] = useState(
EditorState.createEmpty(),
);
const handleChange = ({ currentTarget }) => {
const { name, value } = currentTarget;
setBlogCreate({...blogCreate, [name]: value});
};
const onEditorStateChange = editorState => {
setEditorState(editorState);
};
const handleSubmit = async event => {
event.preventDefault();
const data = draftToHtml(convertToRaw(editorState.getCurrentContent()));
try {
await blogAPI.create(blogCreate, data);
} catch (error) {
console.log(error)
}
}
console.log(data);
}
return(
<Field type="text" name="title" error={errors.title} value={blogCreate.title}
onChange={handleChange}
/>
<Editor name="description" editorState={editorState} onEditorStateChange={editorState => onEditorStateChange(editorState)}
/>
<button type="submit">Save</button>
);
}
export default BlogCreate;
Based on the full code you've provided me, I realised that you aren't properly updating the blogCreate state whenever there is a change at the Editor component.
The onEditorStateChange() should be updating the blogCreate state, and in addition, changeValue() needs to return the result value.
const changeValue = editorState => {
const value = ....
return value;
};
const onEditorStateChange = editorState => {
const description = changeValue(editorState);
setBlogCreate({
...blogCreate,
description,
});
setEditorState(editorState);
};
This way, description will be properly updated on your state, and it will be sent to your server side when you make the blogAPI.create() request.
Related
Whenever I dispatch a search action using context and useReducer for an object in an array stored in local storage, it returns the object, but when I delete the search query from the input box, the list is not returned and the page is blank, can anyone help please?
This is my context:
const NotesContext = createContext(null);
const NotesDispatchContext = createContext(null);
const getStoredNotes = (initialNotes = InitialNotes) => {
return JSON.parse(localStorage.getItem("storedNotes")) || initialNotes;
};
export const NotesProvider = ({ children }) => {
const [NOTES, dispatch] = useReducer(NotesReducer, getStoredNotes());
useEffect(() => {
localStorage.setItem("storedNotes", JSON.stringify(NOTES));
}, [NOTES]);
return (
<NotesContext.Provider value={NOTES}>
<NotesDispatchContext.Provider value={dispatch}>
{children}
</NotesDispatchContext.Provider>
</NotesContext.Provider>
);
};
export const useNotesContext = () => {
return useContext(NotesContext);
};
export const useNotesDispatchContext = () => {
return useContext(NotesDispatchContext);
};
const App = () => {
const [query, setQuery] = useState("");
const dispatch = useNotesDispatchContext();
useEffect(() => {
if (query.length !== 0) {
dispatch({
type: "searchNotes",
query: query,
});
}
}, [query]);
return (
<div className="container">
<header>
<Title title={"Notes"} className={"app_title"} />
<form className="search_container">
<span class="material-symbols-outlined">search</span>
<input
type="search"
placeholder="search notes"
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
</form>
</header>
This is my reducer function
case "searchNotes": {
[...NOTES].filter((note) =>
note.title.toLowerCase().includes(action.query)
);
}
The function seems to actually remove the all data from the local storage instead of filtering based on the query string.
Issue
When you dispatch searchNotes you are changing NOTES and the blow useEffect runs. So if the filter resulted to an empty array, there would be nothing in localStorage.
useEffect(() => {
localStorage.setItem("storedNotes", JSON.stringify(NOTES));
}, [NOTES]);
Solution
What you can do is to remove that useEffect in App that has query as dependency and dispatching searchNotes. And filter directly while rendering, something like this:
{
NOTES.filter((note) => note.title.toLowerCase().includes(query)).map((note, index) => (
<div key={index}>{note.title}</div>
))
}
And at this point you can remove searchNotes case from your reducer.
I'm new to Redux work, trying to learn by doing. Here I have AntD input, when user writes something then it saves it to the object keys billingName: and billingContactPerson, but I have also two buttons sender and receiver, when user clicks sender button then it takes data from redux and put it to input, but my question is how to save that data to the same billingName and billingContactPerson. I have tried to save it in useEffect billingName = PickUpName, but it did not save it.
My code:
let billingName: any;
let billingContactPerson: any;
const userData = useSelector(selectUserData);
const dispatch = useDispatch();
const DeliveryName = userData.deliveryName;
const PickUpName = userData.pickUpName;
const DeliveryContactPerson = userData.deliveryContactPerson;
const PickUpContactPerson = userData.pickUpContactPerson;
const [name, setName] = useState(billingName);
const [contactPerson, setContactPerson] = useState(
billingContactPerson
);
const [payer, setPayer] = useState("");
useEffect(() => {
const names = () => {
if (payer === "receiver") {
billingName = DeliveryName;
dispatch(changeUserData({ ...userData, billingName }));
}
if (payer === "sender") {
billingName = PickUpName;
dispatch(changeUserData({ ...userData, billingName }));
} else {
return billingName;
}
};
setName(names);
const contactPersons = () => {
if (payer === "receiver") {
billingContactPerson = DeliveryContactPerson;
dispatch(changeUserData({ ...userData, billingContactPerson }));
}
if (payer === "sender") {
billingContactPerson = PickUpContactPerson;
dispatch(changeUserData({ ...userData, billingContactPerson }));
} else {
return billingContactPerson;
}
};
setContactPerson(contactPersons);
}, [payer]);
const senderPays = (e: any) => {
e.preventDefault();
setPayer("sender");
};
const receiverPays = (e: any) => {
e.preventDefault();
setPayer("receiver");
};
<div>
<Button onClick={senderPays}>sender</Button>
<Button onClick={receiverPays}>receiver</Button>
<Form.Item
label={t("o.billingName")}
name="billingName"
initialValue={userData["billingName"] || name || ""}
>
<Input
onChange={(e: any) =>
dispatch(
changeUserData({ ...userData, billingName: e.target.value })
)
}
type="string"
/>
</Form.Item>
<Form.Item
label={t("orders.ContactPerson")}
name="billingContactPerson"
initialValue={
userData["billingContactPerson"] ||
contactPerson ||
""
}
>
<Input
onChange={(e: any) =>
dispatch(
changeUserData({
...userData,
billingContactPerson: e.target.value,
})
)
}
type="string"
/>
</Form.Item>
</div>
If you're new I recommend you to start with reduxjs/toolkit. It is the new recommended way of writting redux logic.
Let's Learn modern redux
About the question you asked. You can try triggering an function after the redux logic or after sending the data to the input field and give type to billingName and billingContactPerson. So, that you can more catch errors.
import React, { useEffect, useState } from "react";
import { dbService } from "./myFirebase";
function Editor({ userObj }) {
const [myContent, setMyContent] = useState("");
const [myContents, setMyContents] = useState([]);
const getValue = (event) => {
const { name, value } = event.target;
setMyContent({ ...myContent, [name]: value });
};
useEffect(() => {
console.log("1", myContents);
dbService.collection("contents").onSnapshot((snapshot) => {
const communityArray = snapshot.docs.map((doc) => ({
id: doc.id,
...doc.data(),
}));
setMyContents(communityArray);
console.log("2", myContents);
});
console.log("3", myContents);
}, []);
console.log("4", myContents);
const addContent = async (event) => {
event.preventDefault();
await dbService.collection("contents").add({
content: myContent,
createdAt: Date.now(),
});
setMyContent("");
};
return (
<div>
<button type="submit" onClick={addContent}>
{" "}
input{" "}
</button>
</div>
);
}
export default Editor;
When data is entered in this way, I coded it to be saved and received in Firebase.
If I print the log, it comes out as an empty array and then the value is entered.
So when I get it, if I say console.log("10", myContents[0].id);, it is properly received, but when I refresh it, TypeError: Cannot read property'id' of undefined appears.
I think it's because of that empty array, how do I fix it?
Are you using async await for useEffect? I tried, but it was the same, was it wrong?
I have an input field which is written in a child component
and its inside return function
const EditSectionComponent = ({
editCaption,
editName,
}) => {
const {name,caption} = details;
return (
<input
type="text"
className="imageNameDetails"
value={name}
onChange={e => editName(e.target)}
/>
)
}
and in parent Component, it's like
const onEditClick = id => {
const selectedAsset = All.find(i => i.id === id);
setDetails(selectedAsset);
}
const [details, setDetails] = useState([]);
const editName = target => {
setDetails({ ...details, [name]: target.value })
};
Initial page load I can see both caption and name in the text field, but I am not able to change its value
It's not updating the UI. Is the right way to reflect the newly edited text in the input field
Make sure to destructure the details from props in EditSectionComponent.
In your parent, the initial state i.e. details is defined as an array. It need to be an object.
Also while doing setDetails, you need to specify the key. (not a dynamic name in your case)
Updated code is here:
const EditSectionComponent = ({
editCaption,
editName,
details,
}) => {
const {name,caption} = details;
return (
<input
type="text"
className="imageNameDetails"
value={name}
onChange={e => editName(e.target)}
/>
)
}
const [details, setDetails] = useState({name: '', caption: ''});
const editName = target => {
setDetails(prev => ({...prev, name: target.value}))
};
I'm using React right now and I'm trying to get my localstorage to update a state once the event handles a return on search and then hold that state until the next search is completed. Right now I can't figure out where to put an event handler that triggers the correct state and holds the correct value.
const useStateWithLocalStorage = localStorageKey => {
const [value, setValue] = React.useState(
localStorage.getItem(localStorageKey) || ''
);
React.useEffect(() => {
localStorage.setItem(localStorageKey, value);
}, [value]);
return [value, setValue];
};
export default function App() {
const [value, setValue] = useStateWithLocalStorage(
'myValueInLocalStorage'
);
const onChange = event => setValue(event.target.value);
const [state, setState] = useState({
message: 'test deploy',
results: [],
value: '',
});
...
and where I'm trying to implement the event handler
export default function SearchAppBar(props) {
const classes = useStyles();
const [searchTerm, setSearchTerm] = useState('');
const { onClick } = props;
...
<InputBase
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Search…"
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
inputProps={{ 'aria-label': 'search' }}
/>
<Button onClick={() => onClick(searchTerm)}> Search </Button>```
Hereby my solution. I've created an useLocalStorage function that stores and gets or sets items in the local storage and holds them in its own state:
import React from "react";
export const useLocalStorage = (key, initialValue) => {
const [storedValue, setStoredValue] = React.useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.log(error);
return initialValue;
}
});
const setValue = value => {
try {
setStoredValue(value);
window.localStorage.setItem(key, JSON.stringify(value));
} catch (error) {
console.log(error);
}
};
return [storedValue, setValue];
};
export default useLocalStorage;
For the searchBar component I've used a forwardRef to access the value of the input inside our higher component App. The newSearch function and searchTerm variable are destructured off the props. The placeholder holds the stored value in localStorage, which is searchTerm:
export const SearchAppBar = React.forwardRef(
({ newSearch, searchTerm }, ref) => {
return (
<>
<input ref={ref} type="text" placeholder={searchTerm} />
<button onClick={newSearch}> Search </button>
</>
);
}
);
Inside the main App component I'm using our useLocalStorage function hook to get and set the search. Inside newSearch I'm updating the search term by calling our hook with the value of the forwarded input ref.
export default function App() {
const ref = React.createRef();
const [searchTerm, setSearchTerm] = useLocalStorage(
"search",
"Not searched yet"
);
const newSearch = () => {
setSearchTerm(ref.current.value);
};
return (
<>
<SearchAppBar ref={ref} newSearch={newSearch} searchTerm={searchTerm} />
<p>Last search: {searchTerm}</p>
</>
);
}
Hope this is a workable solution for you.
Please find a code snippet here:
https://codesandbox.io/s/cranky-sunset-8fqtm?file=/src/index.js:387-773
I like the approach used by redux to handling the states on react. I use redux with redux-persist library to save the state instead of localStorage. If your project grows and you need to work with more complex states, it could help you.