[英]Reactjs closure when passing state to component
I got a react functional component:我有一个反应功能组件:
const DataGrid = (props) =>
{
const [containerName, setContainerName] = useState("");
const [frameworkComponents, setFrameworkComponents] = useState(
{customLoadingOverlay: LoadingOverlayTemplate,
customNoRowsOverlay: UxDataGridCustomNoRows,
editButton: params => <ViewAndDeleteSetting {...params}
openAddConfigurationsWindow={openAddConfigurationsWindow}
onDeleteSetting={onDeleteSetting}/>,
});
useEffect(async () =>
{
if(props.containerName && props.containerName !== "")
{
setContainerName(props.containerName);
}
},[props.containerName]);
.
.
.
const onDeleteSetting = async (settingKey) =>
{
console.log("ON DELETE AND CONTAINER NAME:");
console.log(containerName); //HERE THE CONTAINER NAME IS EMPTY
...
}
return (
<UxDataGrid
frameworkComponents={frameworkComponents}/>
);
The container name inside useEffect exists and is not empty. useEffect 中的容器名称存在且不为空。 As you can see in the comment in onDeleteSetting
, the containerName is empty when this callback is invoked.正如您在onDeleteSetting
的注释中看到的那样,调用此回调时 containerName 为空。 I tried adding this to the useEffect
after setContainerName
:我尝试在setContainerName
之后将此添加到useEffect
:
setFrameworkComponents({customLoadingOverlay: LoadingOverlayTemplate,
customNoRowsOverlay: UxDataGridCustomNoRows,
editButton: params => <ViewAndDeleteSetting {...params}
openAddConfigurationsWindow={openAddConfigurationsWindow}
onDeleteSetting={onDeleteSetting}/>,
});
That didn't work.那没有用。
How can I get the name inside the callback?如何在回调中获取名称? There is no special need to leave that frameworkComponents
struct in the state.. it can also be moved to somewhere else if you think its better没有特别需要将frameworkComponents
结构留在 state 中。如果您认为它更好,它也可以移动到其他地方
You almost certainly shouldn't be storing it in state.您几乎可以肯定不应该将其存储在 state 中。 Props are essentially state controlled by the parent. Props 本质上是由父级控制的 state。 Just use it from props.只需从道具中使用它。 Copying props to state is usually not best practice .将道具复制到 state 通常不是最佳实践。
If you're looking at one of the very rare situations where it makes sense to set derived state based on props, this page in the documentation tells you how to do that with hooks.如果您正在查看一种非常罕见的情况,其中基于 props 设置派生 state 是有意义的,文档中的此页面将告诉您如何使用钩子来做到这一点。 Basically, you don't use useEffect
, you do your state update right away.基本上,您不使用useEffect
,而是立即进行 state 更新。
Here's a full quote from the linked documentation:这是链接文档的完整引用:
How do I implement getDerivedStateFromProps?如何实现 getDerivedStateFromProps?
While you probably don't need it , in rare cases that you do (such as implementing a
<Transition>
component), you can update the state right during rendering.虽然您可能不需要它,但在极少数情况下您需要它(例如实现<Transition>
组件),您可以在渲染期间立即更新 state。 React will re-run the component with updated state immediately after exiting the first render so it wouldn't be expensive. React 将在退出第一个渲染后立即使用更新的 state 重新运行组件,因此不会很昂贵。Here, we store the previous value of the row prop in a state variable so that we can compare:在这里,我们将 row prop 的先前值存储在 state 变量中,以便我们进行比较:
function ScrollView({row}) { const [isScrollingDown, setIsScrollingDown] = useState(false); const [prevRow, setPrevRow] = useState(null); if (row.== prevRow) { // Row changed since last render. Update isScrollingDown; setIsScrollingDown(prevRow;== null && row > prevRow): setPrevRow(row); } return `Scrolling down: ${isScrollingDown}`; }
This might look strange at first, but an update during rendering is exactly what getDerivedStateFromProps has always been like conceptually.起初这可能看起来很奇怪,但渲染期间的更新正是 getDerivedStateFromProps 在概念上一直以来的样子。
If you did it the same way they did in that example, your component would still render with containerName
set to the default state ( ""
), it's just that it will then almost immediately re-render with the updated containerName
.如果您按照他们在该示例中的操作方式进行操作,您的组件仍将使用设置为默认 state ( ""
) 的containerName
进行渲染,只是它几乎会立即使用更新后的containerName
重新渲染。 That makes sense for their example of a transition, but you could avoid that by making the prop's initial value the state's initial value, like this:这对于他们的过渡示例是有意义的,但是您可以通过将道具的初始值设为状态的初始值来避免这种情况,如下所示:
const DataGrid = (props) => {
const [containerName, setContainerName] = useState(props.containerName); // *** ONLY USES THE INITIAL PROP VALUE
const [frameworkComponents, setFrameworkComponents] = useState(
// ...
});
// *** Updates the state value (on the next render) if the prop changes
if (containerName !== props.containerName) {
setContainerName(props.containerName);
}
// ...
};
Every time the containerName
prop changes, though, your component will render twice, which brings us back full circle to: Don't store it in state, just use it from props.但是,每次containerName
属性更改时,您的组件都会渲染两次,这让我们回到了原点:不要将其存储在 state 中,只需从属性中使用它即可。 :-) :-)
Stepping back and looking at the component as a whole, I don't think you need any state information at all, but if your goal is to avoid having the frameworkComponents
you pass UxDataGrid
change unnecessarily, you probably want useMemo
or React.memo
rather than state.退一步看整个组件,我认为您根本不需要任何 state 信息,但如果您的目标是避免让您传递UxDataGrid
的frameworkComponents
发生不必要的更改,您可能需要useMemo
或React.memo
而不是state。
For instance, with useMemo
(but keep reading):例如,使用useMemo
(但请继续阅读):
const DataGrid = ({containerName}) => {
const frameworkComponents = useMemo(() => {
const onDeleteSetting = async (settingKey) => {
console.log("ON DELETE AND CONTAINER NAME:");
console.log(containerName);
// ...
};
return {
customLoadingOverlay: LoadingOverlayTemplate,
editButton: params => <ViewAndDeleteSetting {...params}
openAddConfigurationsWindow={openAddConfigurationsWindow}
onDeleteSetting={onDeleteSetting} />,
};
}, [containerName]);
return (
<UxDataGrid frameworkComponents={frameworkComponents} />
);
};
But if componentName
is your only prop, it may well be even simpler with React.memo
:但是如果componentName
是你唯一的道具,那么使用React.memo
可能会更简单:
const DataGrid = React.memo(({containerName}) => {
const onDeleteSetting = async (settingKey) => {
console.log("ON DELETE AND CONTAINER NAME:");
console.log(containerName);
// ...
};
return (
<UxDataGrid frameworkComponents={{
customLoadingOverlay: LoadingOverlayTemplate,
editButton: params => <ViewAndDeleteSetting {...params}
openAddConfigurationsWindow={openAddConfigurationsWindow}
onDeleteSetting={onDeleteSetting} />,
}} />
);
});
React.memo
memoizes your component, so that your component function is only ever called again when the props change. React.memo
记住您的组件,以便您的组件 function 仅在道具更改时才会再次调用。 Since everything in the component needs to update based on the componentName
prop changing, that looks like a good match (but I don't know what UxDataGrid
is).由于组件中的所有内容都需要根据componentName
属性的变化进行更新,这看起来很匹配(但我不知道UxDataGrid
是什么)。
Try this in your useEffect, update the onDeleteSetting function with the new containerName when it's updated在你的 useEffect 中试试这个,更新 onDeleteSetting function 时使用新的 containerName
.....
useEffect(async() => {
if (props.containerName && props.containerName !== "") {
setContainerName(props.containerName);
// move this function here
const onDeleteSetting = async(settingKey) => {
console.log("ON DELETE AND CONTAINER NAME:");
// use props.containerName since the state update is async
console.log(props.containerName);
...
}
// update your components with the updated functions
setFrameworkComponents(prevComponents => ({
...prevComponents,
editButton: params =>
<ViewAndDeleteSetting
{...params}
openAddConfigurationsWindow={openAddConfigurationsWindow}
onDeleteSetting={onDeleteSetting}
/>,
}));
}
}, [props.containerName]);
.....
This should provide the updated state with the updated function, if it works, I can add more details.这应该提供更新的 state 和更新的 function,如果有效,我可以添加更多细节。
The problem was with how I tried passing props to ViewAndDeleteSetting
.问题在于我如何尝试将道具传递给ViewAndDeleteSetting
。 If you want to pass prop to a cell rendered component, you shouldn't be doing it in frameworkComponents
, but rather you need to do it in the column definition like this:如果要将 prop 传递给单元格渲染组件,则不应在frameworkComponents
中执行此操作,而是需要在列定义中执行此操作,如下所示:
useEffect(() =>
{
let columns = [{headerName: '', cellRenderer: 'editButton', width: 90, editable: false,
cellRendererParams: {
openAddConfigurationsWindow: openAddConfigurationsWindow,
onDeleteSetting: onDeleteSetting
}},
.. other columns
]
setColumnDefinition(columns);
},[props.containerName]);
The columns with the cellRendererParams
do gets recreated in the useEffect
when the name changes, and then the component can access this params regularly via its props当名称更改时,具有cellRendererParams
的列会在useEffect
中重新创建,然后组件可以通过其 props 定期访问此参数
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.