I'm confused about the refactoring of my code.
My Component works like this ==> I set the incoming props from the parent component to the child component's state. Then I can do the update state operations in the component.
This is my UNSAFE_componentWillReceiveProps
function:
UNSAFE_componentWillReceiveProps = (nextProps) => {
if (nextProps
&& nextProps.actionToEdit
&& nextProps.actionToEdit.statement
) {
const regex = /@\w+/g;
const found = nextProps.actionToEdit.statement.match(regex);
this.setState({
parameters: found
});
}
};
This is my getDerivedStateFromProps
function:
static getDerivedStateFromProps(
nextProps: ICustomActionModalProps,
prevState: ICustomActionModalState
) {
if (nextProps
&& nextProps.actionToEdit
&& nextProps.actionToEdit.statement
) {
const regex = /@\w+/g;
const found = nextProps.actionToEdit.statement.match(regex);
return {
parameters: found
};
}
return null;
}
This is my onChange function:
onChange = (newValue, e) => {
const regex = /@\w+/g;
const found = newValue.match(regex);
this.setState({ parameters: found });
};
I realized something when onChange worked.
==> If I use UNSAFE_componentWillReceiveProps
, the state updated perfectly. Because when the onChange function works every time UNSAFE_componentWillReceiveProps
doesn't work.
However,
==> If I use getDerivedStateFromProps
, the state updated with old props. Because when the onChange function works every time getDerivedStateFromProps
works too.
I just want my getDerivedStateFromProps
function will able to works like my old UNSAFE_componentWillReceiveProps
function.
How can I do that? Thanks
The problem you're describing is solved either by making your Component
fully controlled or fully uncontrolled but with a key.
The first approach may look like this:
// --- parent component: Parent.tsx
import { Child } from "./Child";
export class Parent extends React.Component<{}, { counter: number }> {
state = { counter: 0 };
render() {
return (
<div>
<div>
<button onClick={() => this.setState(({ counter }) => ({ counter: counter + 1 }))}>increase counter</button>
</div>
<Child
parentCounter={this.state.counter}
setParentCounter={(n) => this.setState({ counter: n })}
/>
</div>
);
}
}
// --- child component : Child.tsx
type Props = { parentCounter: number; setParentCounter: (n: number) => void };
export class Child extends React.Component<Props> {
render() {
const { parentCounter, setParentCounter } = this.props;
return (
<>
<div>parent counter: {parentCounter}</div>
<button
onClick={() => {
setParentCounter(parentCounter + 1);
}}
>
increase parent counter
</button>
</>
);
}
}
So, there is not two separate states: one in the parent component and another in the child one. The only state exists in the parent component and child component has the setter prop it may use to change parent's state.
The second approach (uncontrolled component with a key) uses the fact that when the key
changes the child component rerenders from scratch and looses it's inner state:
// --- Parent.tsx
import { Child } from "./Child";
export class Parent extends React.Component<{}, { counter: number }> {
state = { counter: 0 };
render() {
const { counter } = this.state;
return (
<div>
<div>parent counter: {counter}</div>
<div>
<button
onClick={() =>
this.setState(({ counter }) => ({ counter: counter + 1 }))
}
>
increase parent counter
</button>
</div>
<Child parentCounter={counter} key={`child-key-${counter}`} />
</div>
);
}
}
// --- Child.tsx
type Props = { parentCounter: number };
type State = { childCounter: number };
export class Child extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { childCounter: props.parentCounter };
}
render() {
return (
<>
<div>child counter {this.state.childCounter}</div>
<button
onClick={() => {
this.setState(({ childCounter }) => ({
childCounter: childCounter + 1
}));
}}
>
increase child counter
</button>
</>
);
}
}