将状态传递给组件时 Reactjs 关闭
Reactjs closure when passing state to component
我得到了一个 React 功能组件:
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}/>
);
useEffect中的容器名存在且不为空。正如您在 onDeleteSetting
中的注释中看到的,调用此回调时 containerName 为空。我尝试在 setContainerName
:
之后将其添加到 useEffect
setFrameworkComponents({customLoadingOverlay: LoadingOverlayTemplate,
customNoRowsOverlay: UxDataGridCustomNoRows,
editButton: params => <ViewAndDeleteSetting {...params}
openAddConfigurationsWindow={openAddConfigurationsWindow}
onDeleteSetting={onDeleteSetting}/>,
});
那没用。
如何在回调中获取名称?没有特别需要将 frameworkComponents
结构保留在状态中.. 如果您认为它更好
也可以将其移动到其他地方
您几乎可以肯定不应该将其存储在状态中。道具基本上是由 parent 控制的状态。只需从道具中使用它。将道具复制到状态是 usually not best practice.
如果您正在寻找一种非常罕见的情况,在这种情况下,根据 props 设置派生状态是有意义的,文档中的 this page 会告诉您如何使用挂钩来做到这一点。基本上,您不使用 useEffect
,而是立即进行状态更新。
以下是链接文档的完整引述:
How do I implement 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. React will re-run the component with updated state immediately after exiting the first render so it wouldn’t be expensive.
Here, we store the previous value of the row prop in a state variable so that we can compare:
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.
如果您按照他们在该示例中所做的方式进行操作,您的组件仍会在 containerName
设置为默认状态 (""
) 的情况下呈现,只是它几乎会立即呈现re-render 已更新 containerName
。这对于他们的过渡示例是有意义的,但是您可以通过使道具的 initial 值成为状态的 initial 值来避免这种情况,如下所示:
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);
}
// ...
};
但是,每次 containerName
道具发生变化时,您的组件都会渲染两次,这使我们回到了完整的循环:不要将其存储在状态中,只需从道具中使用它。 :-)
回过头来看组件作为一个整体,我认为您根本不需要任何状态信息,但是如果您的目标是避免 frameworkComponents
您通过 UxDataGrid
进行不必要的更改,您可能需要 useMemo
or React.memo
而不是 state.
例如,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} />
);
};
但是如果 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
记忆你的组件,这样你的组件函数只会在 props 改变时再次调用。由于组件中的所有内容都需要根据 componentName
属性的变化进行更新,因此看起来很匹配(但我不知道 UxDataGrid
是什么)。
在你的 useEffect 中试试这个,更新时用新的 containerName 更新 onDeleteSetting 函数
.....
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]);
.....
这应该提供具有更新功能的更新状态,如果有效,我可以添加更多详细信息。
问题在于我如何尝试将道具传递给 ViewAndDeleteSetting
。如果你想将 prop 传递给单元格渲染组件,你不应该在 frameworkComponents
中这样做,而是需要在列定义中这样做:
useEffect(() =>
{
let columns = [{headerName: '', cellRenderer: 'editButton', width: 90, editable: false,
cellRendererParams: {
openAddConfigurationsWindow: openAddConfigurationsWindow,
onDeleteSetting: onDeleteSetting
}},
.. other columns
]
setColumnDefinition(columns);
},[props.containerName]);
带有 cellRendererParams
的列会在名称更改时在 useEffect
中重新创建,然后组件可以通过其 props
定期访问此参数
我得到了一个 React 功能组件:
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}/>
);
useEffect中的容器名存在且不为空。正如您在 onDeleteSetting
中的注释中看到的,调用此回调时 containerName 为空。我尝试在 setContainerName
:
useEffect
setFrameworkComponents({customLoadingOverlay: LoadingOverlayTemplate,
customNoRowsOverlay: UxDataGridCustomNoRows,
editButton: params => <ViewAndDeleteSetting {...params}
openAddConfigurationsWindow={openAddConfigurationsWindow}
onDeleteSetting={onDeleteSetting}/>,
});
那没用。
如何在回调中获取名称?没有特别需要将 frameworkComponents
结构保留在状态中.. 如果您认为它更好
您几乎可以肯定不应该将其存储在状态中。道具基本上是由 parent 控制的状态。只需从道具中使用它。将道具复制到状态是 usually not best practice.
如果您正在寻找一种非常罕见的情况,在这种情况下,根据 props 设置派生状态是有意义的,文档中的 this page 会告诉您如何使用挂钩来做到这一点。基本上,您不使用 useEffect
,而是立即进行状态更新。
以下是链接文档的完整引述:
How do I implement 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. React will re-run the component with updated state immediately after exiting the first render so it wouldn’t be expensive.Here, we store the previous value of the row prop in a state variable so that we can compare:
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.
如果您按照他们在该示例中所做的方式进行操作,您的组件仍会在 containerName
设置为默认状态 (""
) 的情况下呈现,只是它几乎会立即呈现re-render 已更新 containerName
。这对于他们的过渡示例是有意义的,但是您可以通过使道具的 initial 值成为状态的 initial 值来避免这种情况,如下所示:
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);
}
// ...
};
但是,每次 containerName
道具发生变化时,您的组件都会渲染两次,这使我们回到了完整的循环:不要将其存储在状态中,只需从道具中使用它。 :-)
回过头来看组件作为一个整体,我认为您根本不需要任何状态信息,但是如果您的目标是避免 frameworkComponents
您通过 UxDataGrid
进行不必要的更改,您可能需要 useMemo
or React.memo
而不是 state.
例如,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} />
);
};
但是如果 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
记忆你的组件,这样你的组件函数只会在 props 改变时再次调用。由于组件中的所有内容都需要根据 componentName
属性的变化进行更新,因此看起来很匹配(但我不知道 UxDataGrid
是什么)。
在你的 useEffect 中试试这个,更新时用新的 containerName 更新 onDeleteSetting 函数
.....
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]);
.....
这应该提供具有更新功能的更新状态,如果有效,我可以添加更多详细信息。
问题在于我如何尝试将道具传递给 ViewAndDeleteSetting
。如果你想将 prop 传递给单元格渲染组件,你不应该在 frameworkComponents
中这样做,而是需要在列定义中这样做:
useEffect(() =>
{
let columns = [{headerName: '', cellRenderer: 'editButton', width: 90, editable: false,
cellRendererParams: {
openAddConfigurationsWindow: openAddConfigurationsWindow,
onDeleteSetting: onDeleteSetting
}},
.. other columns
]
setColumnDefinition(columns);
},[props.containerName]);
带有 cellRendererParams
的列会在名称更改时在 useEffect
中重新创建,然后组件可以通过其 props