admin管理员组文章数量:1316329
I went through several questions on SO regarding default props for functional ponents and they all remend using ES6 default parameters. Here are links to those questions.
- React - defaultProps vs ES6 default params when destructuring (performances issues)
- React functional ponent default props vs default parameters
However, when I use that method for writing ponents with effects running on props change, I get unwanted behaviour with non-primitives. For example, the following code will result in an infinite loop.
const Parent = () => {
let somethingUndefined;
return (
<div>
<Child prop={somethingUndefined} />
</div>
);
};
const Child = ({ prop = {a: 1} }) => {
const [x, setX] = React.useState(1);
React.useEffect(() => {
setX(x + 1);
}, [prop]);
return <div>{x}, {prop.a}</div>;
};
ReactDOM.render(<Parent />, document.getElementsByTagName('body')[0]);
<script src=".11.0/umd/react.production.min.js"></script>
<script src=".11.0/umd/react-dom.production.min.js"></script>
I went through several questions on SO regarding default props for functional ponents and they all remend using ES6 default parameters. Here are links to those questions.
- React - defaultProps vs ES6 default params when destructuring (performances issues)
- React functional ponent default props vs default parameters
However, when I use that method for writing ponents with effects running on props change, I get unwanted behaviour with non-primitives. For example, the following code will result in an infinite loop.
const Parent = () => {
let somethingUndefined;
return (
<div>
<Child prop={somethingUndefined} />
</div>
);
};
const Child = ({ prop = {a: 1} }) => {
const [x, setX] = React.useState(1);
React.useEffect(() => {
setX(x + 1);
}, [prop]);
return <div>{x}, {prop.a}</div>;
};
ReactDOM.render(<Parent />, document.getElementsByTagName('body')[0]);
<script src="https://cdnjs.cloudflare./ajax/libs/react/16.11.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare./ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script>
I attempted two ways of attempting to circumvent the issue. First, by just assigning a different variable that contains the default, and putting the unmodified prop in the dependency array. ie
const Child = ({ prop }) => {
const [x, setX] = React.useState(1);
const defaultedProp = prop || {a: 1};
React.useEffect(() => {
setX(x + 1);
}, [prop]);
// Note we use prop and not defaultedProp here to avoid runnning into the issue above.
return <div>{x}, {defaultedProp.a}</div>;
};
Another method would be to just use something like (prop || {a:1})
in place of prop
everywhere you use it, except in the dependency array. ie
const Child = ({ prop }) => {
const [x, setX] = React.useState(1);
React.useEffect(() => {
setX(x + 1);
}, [prop]);
return <div>{x}, {(prop || {a: 1}).a}</div>;
};
But both of these solutions seem suboptimal since it would require a lot of wasted effort (and bulky code).
defaultProps
is also a solution to the infinite loop issue but it is deprecated. Note that the example provided in this rfc also uses ES6 default parameters in the code.
Am I missing something? Is there a better way to use default props in stateful functional ponents that run effects on props change?
Share Improve this question edited Feb 3, 2020 at 7:47 Louis Christopher 3685 silver badges18 bronze badges asked Jan 30, 2020 at 11:16 ManavMManavM 3,1082 gold badges22 silver badges33 bronze badges 3- Why not pass default props directly from parent itself? – kooskoos Commented Jan 30, 2020 at 11:19
-
@Then that would be the default behaviour for the
Parent
ponent. We would want theChild
ponent to have these defaults (Since a child ponent would most likely be reusable and need the same set of defaults everywhere). – ManavM Commented Jan 30, 2020 at 11:27 -
Long live
defaultProps
. – KFunk Commented Nov 2, 2020 at 22:51
4 Answers
Reset to default 8 +50I don't know whether this is eligible for an answer but all your concerns could be resolved by declaring your default value as a constant in the app. That means;
const Parent = () => {
const somethingUndefined;
return (
<>
<Child prop={somethingUndefined} />
</>
);
};
const Child = ({ prop = {a: 1} }) => {
const [x, setX] = React.useState(1);
React.useEffect(() => {
setX(x + 1);
}, [prop]);
return <div>{x}, {prop.a}</div>;
};
You can change the above code to
const Parent = () => {
const somethingUndefined;
return (
<>
<Child prop={somethingUndefined} />
</>
);
};
const defaultPropValue = {a: 1};
const Child = ({ prop = defaultPropValue }) => {
const [x, setX] = React.useState(1);
React.useEffect(() => {
setX(x + 1);
}, [prop]);
return <div>{x}, {prop.a}</div>;
};
This will not cause any infinite loops.
The difference bet these two:- In the first, the prop
is initialized to a new value ie, {a: 1}
and on every state update, this will be a new object (the new object will be in a new memory location), and it invokes the callback again.
In the second, we initialized and assigned the {a: 1}
to defaultPropValue
which will not change. Then we assigned this defaultPropValue
to prop
so that on every re-render, the value assigned to the prop
will be the same ( or from the same memory location). So it works as expected.
Hope the idea is clear!
The useEffect()
will run the first time and invoke the setX()
then:
setX()
will update the state ofx
which will trigger the ponent to re-render again.prop
will receive a new objectconst Child = ({ prop = {a: 1} }) => {
useEffect()
will run again and invoke thesetX()
the whole process repeats again, This causes an infinite loop.
Instead you could pass a default value to a
property and use it in the useEffect()
dependencies array
const Parent = () => {
let somethingUndefined; // babel plains if we use `const` without value
return (
<div>
<Child prop={somethingUndefined} />
<Child prop={{ a: 3 }} />
</div>
);
};
const Child = ({ prop = {} }) => {
const { a = 1 } = prop;
const [x, setX] = React.useState(1);
React.useEffect(() => {
setX(x + 1);
}, [a]);
return <div>{x}, {a}</div>;
};
ReactDOM.render(<Parent />, document.getElementsByTagName('body')[0]);
<script src="https://cdnjs.cloudflare./ajax/libs/react/16.11.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare./ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script>
See https://codepen.io/McKabue/pen/dyPxGLQ?editors=0010
const Parent = () => {
const somethingUndefined = undefined;
return <Child prop={somethingUndefined}/>;
};
const Child = ({ prop = {a: 1} }) => {
const [x, setX] = React.useState(1);
React.useEffect(() => {
setX(prop.a + 1);
});
return <div>{x}, {prop.a}</div>;
};
ReactDOM.render(
<Parent />,
document.getElementById('root')
);
useEffect(() => {
// anything you want to do
, [JSON.stringify(dependencyName)]}
本文标签: javascriptDefault Props in stateful (using hooks) functional componentsStack Overflow
版权声明:本文标题:javascript - Default Props in stateful (using hooks) functional components - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741997069a2410259.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论