我有一个这样的格式的全局状态:
{
type: "container",
items: [
{
type: "box"
},
{
type: "container",
items: [
type: "container",
items: [
{
type: "box",
color: "green"
},
{
type: "box",
color: "red"
},
{
type: "box"
}
]
]
}
]
}
上面的状态将产生下面的输出: output
如您所见,我们只有两个元素:容器和盒子。 容器有按钮,可以包含容器或盒子。 盒子只能改变它们的颜色。 要更好地理解,请观看this demo。
我已经能够复制这种行为,但我希望能够提取应用程序的整个状态并能够恢复它。
恢复状态没有问题。如果我传递像上面这样的 json,它将起作用。 但是当谈到保存我当前的状态时,问题就出现了。 你看,当我尝试恢复我的状态时,我将嵌套对象作为道具传递,然后它成为该子容器的本地状态。但是当我改变那个子容器的状态时,它显然不会改变全局状态(上面的json)。
所以我尝试了两种方法来改变全局状态:
这是我的代码:
onChange
方法,该方法将在某些本地状态更改时触发。然后将该方法作为道具传递,并通过将更改的状态作为参数传递来调用该方法。 (你可以在我的代码中看到这一点)state.items[path[0]].items[path[1]].items[path[n]]
,然后通过 setState
钩子回调手动更改它。在这两种情况下,我都得到
Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
它只是冻结。
Container.js
import React, { useState } from 'react'
import { v4 as uuidv4 } from 'uuid'
import Box from './Box'
function Container({ current, path, onChange }) {
const styles = ... some styles ...
const [popoverVisible, setPopoverVisibility] = useState(false)
const [state, setState] = useState(current || [])
React.useEffect(() => onChange(state, path), [state])
const handleButtonMouseOver = () => setPopoverVisibility(true)
const handleButtonMouseLeave = () => setPopoverVisibility(false)
const handlePopoverMouseOver = () => setPopoverVisibility(true)
const handlePopoverMouseLeave = () => setPopoverVisibility(false)
const props = {
container: { style: styles.container },
wrapper: { style: styles.wrapper },
popover: {
style: styles.popover,
onMouseOver: handlePopoverMouseOver,
onMouseLeave: handlePopoverMouseLeave
},
buttonAdd: {
style: styles.button,
onMouseOver: handleButtonMouseOver,
onMouseLeave: handleButtonMouseLeave
},
buttonBox: {
style: styles.button,
onClick: () => setState([...state, {type: 'box'}])
},
buttonContainer: {
style: styles.button,
onClick: () => setState([...state, {type: 'container', items: []}])
//onClick: () => setState({...state, items: [...state.items, {type: 'container', items: []}]})
}
}
return (
<>
<div {...props.container}>
{state.map((x, i) => x.type === 'container' ? <Container key={uuidv4()} current={x.items} onChange={ onChange } path={[...path, i]}></Container> : <Box key={uuidv4()}></Box>)}
<div {...props.wrapper}>
<div>
<div {...props.popover}>
{popoverVisible && <button {...props.buttonBox}>Box</button>}
{popoverVisible && <button {...props.buttonContainer}>Container</button>}
</div>
<button {...props.buttonAdd}>Add</button>
</div>
</div>
</div>
</>
)
}
export default Container
App.js
import { useState, useEffect } from 'react'
import Container from './Container.js'
function App() {
const styles = {
button: {
margin: ".5em",
width: "7em",
height: "3em",
border: "1px solid black",
borderRadius: ".25em"
}
}
const [state, setState] = useState({type: "container", items: []})
const [newState, setNewState] = useState("")
const [generatedJson, setGeneratedJson] = useState("")
const handleContainerChange = (s, p) => setState({...state, items: s})
const handleTextareaChange = e => setNewState(e.target.value)
const handleBuildButtonClick = () => setState(JSON.parse(newState))
const handleCreateJsonButtonClick = () => setGeneratedJson(JSON.stringify(state))
//useEffect(() => {alert(state)}, [state])
return (
<>
<div style={{ display: "flex" }}>
<Container key={state} current={state.items} path={[]} onChange={ handleContainerChange }>
</Container>
</div>
<textarea cols="30" rows="5" onChange={ handleTextareaChange }></textarea><br/>
<button style={styles.button} onClick={ handleBuildButtonClick }>Build</button><br/>
<button style={styles.button} onClick={ handleCreateJsonButtonClick }>Create JSON</button>
<label key={generatedJson}>{generatedJson}</label>
</>
)
}
export default App
所以我想要的是在本地状态改变时更新全局状态。 示例:
globalState = {
type: "container",
items: [
{ type: "container", items: [] }
]
}
// that inner container from the global state
localState = { type: "container", items: [] }
// so when it changes to
localState = {
type: "container",
items: [
{type: "box"}
]
}
// i want the global state to become
globalState = {
type: "container",
items: [
{
type: "container",
items: [
{type: "box"}
]
}
]
}
我该怎么做?
答案 0 :(得分:0)
我认为主要问题是将 state
用作 Container
中 App
组件上的 React 键。每次 state
更新时,您都会指定一个新的 React 密钥,React 将通过卸载先前版本并安装新版本的 Container
来处理此问题。
<Container
key={state} // <-- new key each state update
current={state.items}
path={[]}
onChange={ handleContainerChange }
>
</Container>
每次 Container
挂载时,它都会实例化 state
并运行 useEffect
,后者调用传递的 onChange
道具。
const [state, setState] = useState(current || []);
React.useEffect(() => onChange(state, path), [state]);
删除 Container
中 App
上的 React 键。
<Container
current={state.items}
path={[]}
onChange={ handleContainerChange }
/>