Redux-Persist Changes State after Application is ReHydrated - reactjs

Working on something similar to Jotform.
This is my state:
const InitialState = {
BaseContainerEl: []
}
This is my approach, let's say I want to create Full Name fields (i.e three inputs with first, middle and last names). So, I create a component that renders a p-tag and onClick it runs the updateBase function where I stored the exact result (i.e the 3 inputs) in a variable 'el' and it update's the state using the virtual DOM created and stored in 'el'.
And it works fine, because after updating the state I render the element.
const FullName = (props: any) => {
const updateBase = () => {
const key = uuidv4();
const el = (
<BaseContainer key={key} id={key}>
<InputCustom
placeholder='First Name'
type='text'
label='First Name'
formControl='form-control form-control-sm'
style={{ width: '90%' }}
/>
<InputCustom
placeholder='Middle Name'
type='text'
label='Middle Name'
formControl='form-control form-control-sm'
style={{ width: '90%' }}
/>
<InputCustom
placeholder='Last Name'
type='text'
label='Last Name'
formControl='form-control form-control-sm'
style={{ width: '90%' }}
/>
</BaseContainer>
);
props.updateBaseContainerEl(el)
}
return (
<p draggable onClick={updateBase} className="text-muted" style={{
borderRadius: '2px',
boxShadow: ' 20px 20px 60px #bebebe-20px -20px 60px #ffffff',
}}>
<i className="fe fe-user" style={{
}}></i>
Full Name
</p>
);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
This the problem:
I am persisting the state using redux-persit, now when I refresh the application it rehydrates and after rehydration the virtual DOM stored in the state (i.e the react component) changes with some properties and values missing. According to the redux-persist docs, there are some values in javascript that cannot be represented in json.
So, it errors out: Uncaught Error: Objects are not valid as a React child (found: object with keys {type, key, ref, props, _owner, _store}). If you meant to render a collection of children, use an array instead.
Below are images of what it looks like before rehydration and after.
Is there any way around this?

Related

How can I create a React functionnal component and get HTML attributes and ref in props with typescript?

I'm trying to make a Trello like, I'm using react-beautiful-dnd and started from the classical example.
This is where I render the card (I just replaced the div with Task, my component)
<Task
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
style={{
userSelect: "none",
padding: 16,
margin: "0 0 8px 0",
minHeight: "50px",
...provided.draggableProps.style,
}}
>
{item.content}
</Task>
In my component, I want to get all div attributes passed in the code above.
So I did this :
import React from "react";
const Task: React.FC<JSX.IntrinsicElements["div"]> = (props) => {
const { children } = props;
return (
<div className="card card-green" {...props}>
<div className="card-content">
<div className="content">{children}</div>
</div>
</div>
);
};
export default Task;
It looks good, excepted for the "ref", I got a message in my console when I try to drag my card
Invariant failed: Cannot get dimension when no ref is set
And indeed my ref is always undefined.
Do you have an idea ?
I tried several examples found but no one works. I think I am missing a type somewhere

React context magically change other state

Can anyone explain this thing? Why does other state magically change value when I modify a context?
title here is modified when I change someState even though these 2 value never interact with each other at all
const LapContext = React.createContext();
function Btn(arg){
return <div onClick={arg.click} className="btn" style={{display: "flex", alignItems: "center", justifyContent: "center", borderRadius: "15px", width: "275px", height: "6em", outline: "1px solid orange"}}>
<p style={{color:"rgb(255,165,0)", fontSize: "2em"}}>{arg.val}</p>
</div>
}
function Control(arg){
let [someState, setSomeState] = React.useContext(LapContext);
function setTitle(){
arg.setter("asdasd");
}
function changeSomeState(){
setSomeState(["a"]);
}
return <div>
<Btn val={"Button1"} click={setTitle}/>
<Btn val={"Button2"} click={changeSomeState}/>
</div>
}
function Main(arg){
{/*this state is modified for no reason*/}
let [title, setTitle] = React.useState("default");
return <div>
<p style={{fontSize: "3em", color: "yellow"}}>{title}</p>
<Control setter={setTitle}/>
</div>
}
function Page(){
{/*this state is shared with context*/}
let [someState, setSomeState] = React.useState(["some value"]);
return <LapContext.Provider value={[someState, setSomeState]}>
<Main key={uuid.v4()}/>
</LapContext.Provider>
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Page/>);
You have quite a few problems in your code, for example, you shouldn't pass imperative API like that, should rely on props/onChange callbacks API instead, it's more React way. Take a look at React docs on how components should interact with each other:
https://reactjs.org/docs/components-and-props.html
But if you just want an answer to the question, then the problem is here:
<Main key={uuid.v4()}/>
You generate a unique key on every re-render. This will cause React to identify the entire component as a "new" component among its siblings, un-mount it, and mount it back from scratch. Which results in your state "resetting" to the default one.
"key" is usually used in the List-like components, you don't need it here. Just remove it and the state will go back to "normal" behaviour.

Strapi onChange signature

I am implementing some minor adjustments to a Strapi CMS, customized by my colleagues.
I need to extend the existing plugins with custom UI [done] and to be able to persist the configuration changes. Found the database config and lifecycle methods. Those functions from my model are never invoked.
I was told that the signature for the onChange method, which my component is receiving in props is onChange({value: newValue}) and it is [excuse my English] the new value for the variable named value from the component properties [props].
const Category = (props) => {
const { name, visible, value, onChange } = props;
...
return (
<div className="container" style={{ margin: '2rem auto', paddingLeft: 0 }}>
<table className="col-12">
{allSubCategories &&
Object.keys(allSubCategories).map((key) => (
<SubCategoryList
key={`sblist ${key}`}
title={key}
visible={visible}
data={allSubCategories[key]}
remainingData={data}
availableAttributes={value}
onChange={(value) => {
console.log('value:', value);
console.log('before onchange');
onChange({value});
console.log('finished onchange');
}}
/>
)
)}
</table>
</div>
);
}
This signature that I am using is not working for me. The call to onChange() crashes with an error: Uncaught TypeError: Cannot read properties of undefined (reading 'name').
What is the correct way of using onChange?
Discovered the correct signature
onChange({
target: {
name,
value,
},
});
Now, it is working.

Objects are not valid as a React child when trying to map antd Select options

I'm trying to ask my api for a list of ids and display them in Select component of antd.
<Select style={{ width: "100%" }}>
{myApi.getIds().then((result) => result.map((value) => {
return <Select.Option value={value}>{value}</Select.Option>
}))}
</Select>
value is string.
getIds returns Promise<string[]>
This returns an error Objects are not valid as a React child (found: [object Promise]). But the value is just a string with an id. What am I missing here?
EDIT: Don't know if it will help anyone, but my solution, based on the accepted answer is:
const [ids, setIds] = useState<string[]>(null);
useEffect(() => {
//some code
if (!ids) {
myApi.getIds().then((result) => setIds(result));
}
// some code
return () => { };
}, [ids]);
// some more code
return (
<>
//render other elements
<Select style={{ width: "100%" }}>
{ids?.map((value) => {
return <Select.Option value={value}>{value}</Select.Option>
})}
</Select>
//render other elements
</>
);
It's not complaining about value, it's complaining about the the return value of then.
<Select style={{ width: "100%" }}>
{myApi.getIds().then((result) => result.map((value) => {
// −^^^^^^^^^^^^^^^^^^^^
return <Select.Option value={value}>{value}</Select.Option>
}))}
</Select>
The return value of then is a promise.
You can't render the result of calling getIds like that. Instead, you'll need to either:
Do the getIds in the parent component and have that component pass the result (when it's available) to this component as a prop,
or
Have this component do getIds and wait for the result in a useEffect callback (functional component w/hooks) or componentDidMount (class component), save the result in state, and use that state when rendering. The component will have to handle rendering when it doesn't have the information yet.

react-dropzone child icon not changing on state change

I have a react project and I am using the react-dropzone component:
import Dropzone from 'react-dropzone';
I want to make it stateful and show different images and text based on the state. I defined my states as:
const status = {
ready: 'ready',
preview: 'preview',
error: 'error',
requested: 'requested',
success: 'success',
failed: 'failed',
};
The state can change based on user actions (so when they drag a file onto the dropzone I update status in state as follows:
onDrop(acceptedFiles, rejectedFiles) {
// do some stuff here...
this.setState({ status: status.preview });
}
My render method is a three step process:
1. the actual render methos
render() {
const config = {
iconFiletypes: ['.xlsx'],
showFiletypeIcon: true,
};
return (
<div style={{ marginBottom: '30px' }}>
<Dropzone
config={config}
onDrop={files => this.onDrop(files)}
//className="dropzone"
multiple={false}
>
{this.renderDropZoneContent()}
</Dropzone>
</div>
);
}
choose what to render based on state:
renderDropZoneContent() {
switch (this.state.status) {
case status.ready:
return this.renderReadyState();
case status.preview:
return this.renderPreviewState();
// and on down for each state / status + default case...
}
}
and finally the code to render each case as functions:
renderPreviewState() {
return (
<div style={{ marginTop: '35px', textAlign: 'center' }}>
<i className="far fa-file-excel" style={{ verticalAlign: 'middle', fontSize: '50px' }} />
{/* There is more jsx here but I removed it for clarity */}
</div>
);
}
renderReadyState() {
return (
<div style={{ marginTop:'35px', textAlign:'center'}>
<i className="fas fa-cloud-upload-alt" style={{ verticalAlign: 'middle', fontSize: '50px' }} />
</div>
);
}
Nothing too crazy. My problem is that as the state changes, the text updates but the icon does not. This is an interesting problem because the logic of the application works, but its the specific element that does not get updated. Even more interesting is that I tried wrapping the entire return in another div and got the error: Uncaught DOMException: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node. I'm banging my head against the wall. If anyone has come across this before and have any tips it is greatly appreciate!!
Probably a conflict with how Font Awesome and React handle rendering.
If you are using React we recommend the react-fontawesome package or Web Fonts with CSS.
https://fontawesome.com/how-to-use/on-the-web/using-with/react

Resources