ChatGPT解决这个技术问题 Extra ChatGPT

如何在 React 中更新嵌套状态属性

我正在尝试使用这样的嵌套属性来组织我的状态:

this.state = {
   someProperty: {
      flag:true
   }
}

但是像这样更新状态,

this.setState({ someProperty.flag: false });

不起作用。如何正确地做到这一点?

你是什么意思不起作用?这不是一个很好的问题——发生了什么?有什么错误吗?什么错误?
嵌套状态是 React JS 中不好的编程风格
应该改用什么?
对于当今 React 的广泛使用,简单地不使用嵌套状态是不可接受的答案。这种情况将会出现,开发人员需要一个答案。

T
Théophile

为了setState嵌套对象,您可以遵循以下方法,因为我认为 setState 不处理嵌套更新。

var someProperty = {...this.state.someProperty}
someProperty.flag = true;
this.setState({someProperty})

这个想法是创建一个虚拟对象对其执行操作,然后用更新的对象替换组件的状态

现在,展开运算符只创建对象的一级嵌套副本。如果您的状态高度嵌套,例如:

this.state = {
   someProperty: {
      someOtherProperty: {
          anotherProperty: {
             flag: true
          }
          ..
      }
      ...
   }
   ...
}

您可以 setState 在每个级别使用扩展运算符,例如

this.setState(prevState => ({
    ...prevState,
    someProperty: {
        ...prevState.someProperty,
        someOtherProperty: {
            ...prevState.someProperty.someOtherProperty, 
            anotherProperty: {
               ...prevState.someProperty.someOtherProperty.anotherProperty,
               flag: false
            }
        }
    }
}))

然而,随着状态变得越来越嵌套,上述语法变得越来越难看,因此我建议您使用 immutability-helper 包来更新状态。

请参阅 this answer,了解如何使用 immutability-helper 更新状态。


@Ohgodwhy,不,这不是直接访问状态,因为 {...this.state.someProperty} 将新对象重新调整为 someProperty 并且我们正在修改它
这对我不起作用..我正在使用反应版本@15.6.1
@Stophface 我们可以使用 Lodash 进行深度克隆,但不是每个人都会包含这个库来设置状态
这不违反 reactjs.org/docs/… 吗?如果嵌套对象的两个更改被批处理,最后一个更改将覆盖第一个更改。所以最正确的方法是:this.setState((prevState) => ({nested: {...prevState.nested, propertyToSet: newValue}})虽然有点乏味......
我认为您不需要最后一个示例中的 ...prevState,,您只需要 someProperty 及其子项
Y
Yoseph

写在一行

this.setState({ someProperty: { ...this.state.someProperty, flag: false} });

建议使用 setState 的更新程序,而不是直接访问 setState 中的 this.state。 reactjs.org/docs/react-component.html#setstate
我相信这就是说,不要在 setState 之后立即阅读 this.state 并期望它具有新值。与在 setState 中调用 this.state 相比。还是后者还有一个我不清楚的问题?
正如trpt4him所说,您提供的链接谈到了在setState之后访问this.state的问题。此外,我们没有将 this.state 传递给 setState 运算符展开属性。它与 Object.assign() 相同。 github.com/tc39/proposal-object-rest-spread
@RaghuTeja 也许只是制作 this.setState(currentState => { someProperty: { ...currentState.someProperty, flag: false} }); 可以避免这个问题?
为了完整起见,我添加了 the answer 如何使用 useState 钩子实现相同的功能。
K
Konstantin Smolyanin

有时直接的答案不是最好的:)

精简版:

这段代码

this.state = {
    someProperty: {
        flag: true
    }
}

应该简化为

this.state = {
    somePropertyFlag: true
}

长版:

目前你不应该在 React 中使用嵌套状态。因为 React 不适合使用嵌套状态,并且这里提出的所有解决方案看起来都是 hack。他们不使用框架,而是与之抗争。他们建议编写不太清晰的代码,用于对某些属性进行分组的可疑目的。因此,它们作为挑战的答案非常有趣,但实际上毫无用处。

让我们想象一下以下状态:

{
    parent: {
        child1: 'value 1',
        child2: 'value 2',
        ...
        child100: 'value 100'
    }
}

如果只更改 child1 的值会发生什么? React 不会重新渲染视图,因为它使用浅比较,并且它会发现 parent 属性没有改变。顺便说一句,直接改变状态对象通常被认为是一种不好的做法。

因此,您需要重新创建整个 parent 对象。但在这种情况下,我们会遇到另一个问题。 React 会认为所有的孩子都改变了他们的价值观,并会重新渲染所有的孩子。当然,这对性能不利。

仍然可以通过在 shouldComponentUpdate() 中编写一些复杂的逻辑来解决该问题,但我更愿意在这里停下来并使用简短版本中的简单解决方案。


我认为在某些用例中嵌套状态非常好。例如,动态跟踪键值对的状态。请参阅此处如何更新状态中单个条目的状态:reactjs.org/docs/…
我想为你建立一个祭坛,并每天早上向它祈祷。我花了三天时间才得出完美解释显而易见的设计决定的答案。每个人都只是因为嵌套状态看起来更易于阅读而尝试使用扩展运算符或进行其他黑客攻击。
那么你如何建议,使用 React 来做一些事情,比如渲染一个交互式树(例如,我的公司在世界不同的地方有一堆传感器,每个传感器都有不同的类型,具有不同的属性,在不同的位置(80+ ),当然,它们是按层次结构组织的,因为每次部署都要花费数千美元,而且是一个完整的项目,因此如果没有层次结构,就不可能管理它们)。我很好奇你怎么不会在 React 中将树之类的东西建模为......树。
似乎 React 并不是为了表示任何存储为非平凡深度的递归结构的东西。这有点令人失望,因为树视图出现在几乎所有复杂的应用程序(gmail、文件管理器、任何文档管理系统......)中。我已经将 React 用于这些事情,但总是有一个 React 狂热者告诉每个人你在做反模式,并建议解决方案是将树的深层副本保持在每个节点的状态(是的,80 个副本)。我很想看到一个不破坏任何 React 规则的非 hacky 树视图组件。
这,一百万次。我花了很多时间弄乱嵌套参数,因为网上无用的评论表明它可能以某种令人困惑的方式出现。 react 文档应该在 setState 部分的开头指出这一点!
Q
Qwerty

免责声明

React 中的嵌套状态是错误的设计阅读这个出色的答案。

这个答案背后的推理:React 的 setState 只是一种内置的便利,但您很快就会意识到它有其局限性。使用自定义属性和智能使用 forceUpdate 可以为您提供更多。例如:class MyClass extends React.Component { myState = someObject inputValue = 42 ... MobX,例如,完全抛弃状态并使用自定义的可观察属性。在 React 组件中使用 Observables 而不是 state。

你的痛苦的答案 - 见这里的例子

还有另一种更短的方法来更新任何嵌套属性。

this.setState(state => {
  state.nested.flag = false
  state.another.deep.prop = true
  return state
})

在一条线上

 this.setState(state => (state.nested.flag = false, state))

注意:这里是 Comma operator ~MDN,查看实际操作 here (Sandbox)

它类似于(尽管这不会改变状态参考)

this.state.nested.flag = false
this.forceUpdate()

有关 forceUpdatesetState 在此上下文中的细微差别,请参阅链接的 examplesandbox

当然这是滥用一些核心原则,因为 state 应该是只读的,但是由于您立即丢弃旧状态并用新状态替换它,所以完全可以。

警告

即使包含状态的组件正确更新和重新渲染(except this gotcha),道具将无法传播给子 (请参阅下面的 Spymaster 评论)。仅当您知道自己在做什么时才使用此技术。

例如,您可以传递一个已更改的平面道具,该道具可以轻松更新和传递。

render(
  //some complex render with your nested state
  <ChildComponent complexNestedProp={this.state.nested} pleaseRerender={Math.random()}/>
)

现在即使 complexNestedProp 的参考没有改变 (shouldComponentUpdate)

this.props.complexNestedProp === nextProps.complexNestedProp

每当父组件更新时,组件重新呈现,在父组件中调用 this.setStatethis.forceUpdate 后就是这种情况。

改变状态沙箱的影响

使用 嵌套状态 并直接改变状态是危险的,因为不同的对象可能(有意或无意地)持有对 state 的不同(旧)引用,并且可能不一定知道何时更新(例如,当使用 PureComponentshouldComponentUpdate 实现返回 false 时)OR 旨在显示旧数据,如下例所示。

想象一个应该呈现历史数据的时间线,改变手下的数据将导致意外行为,因为它也会改变以前的项目。

https://i.stack.imgur.com/Hl7fa.gif

无论如何,在这里您可以看到 Nested PureChildClass 由于 props 未能传播而没有重新渲染。


你不应该在任何地方改变任何反应状态。如果有嵌套组件使用来自 state.nested 或 state.another 的数据,它们将不会重新渲染,它们的子级也不会。这是因为对象没有改变,因此 React 认为没有任何改变。
@tobiv最好将获取时的数据转换为不同的结构,但这取决于用例。如果您将嵌套对象或对象数组视为 some 数据的紧凑单元,则可以将它们置于某个状态。当您需要存储 UI 开关或其他可观察数据(即应该立即更改视图的数据)时,就会出现问题,因为它们必须是平坦的才能正确触发内部 React 差异算法。对于其他嵌套对象的更改,很容易触发 this.forceUpdate() 或实现特定的 shouldComponentUpdate
你应该删除你的陈述“最好的答案”。你怎么知道这是最好的答案?您的陈述具有误导性和过度主张。
有点离题 - 您能否提供一些信息,为什么 this.setState(state => (state.nested.flag = false, state)) 中的 (expression1, expression2) 会这样工作? (文档、mdn、搜索请求等)
@MegaProger 这是一个Comma operator ~MDN。事实上,在 setState 调用中只返回一个空对象 (exp1, {}) 也可以,但我不想不必要地创建新对象。 sandbox
A
Alyssa Roose

如果您使用的是 ES2015,您可以访问 Object.assign。您可以按如下方式使用它来更新嵌套对象。

this.setState({
  someProperty: Object.assign({}, this.state.someProperty, {flag: false})
});

您将更新的属性与现有的属性合并,并使用返回的对象来更新状态。

编辑:向分配函数添加了一个空对象作为目标,以确保状态不会像 carkod 指出的那样直接发生突变。


我可能是错的,但我不认为你在没有 ES2015 的情况下使用 React。
a
alphablend.dev
const newState = Object.assign({}, this.state);
newState.property.nestedProperty = "new value";
this.setState(newState);

这里似乎是更优雅的解决方案,1) 避免在 setState 中进行繁琐的传播;2) 将新状态很好地放入 setState 中,避免直接在 setState 中发生变异。最后但并非最不重要的 3) 避免使用任何库来完成简单的任务
这两个例子是不等价的。如果 property 包含多个嵌套属性,第一个将删除它们,第二个不会。 codesandbox.io/s/nameless-pine-42thu
Qwerty,你是对的,谢谢你的努力。我删除了 ES6 版本。
简单明了!
我用了你的解决方案。我的状态 obj 很小,而且运行良好。 React 会只渲染改变的状态还是渲染所有东西?
J
Joakim Jäderberg

我们使用 Immer https://github.com/mweststrate/immer 来处理这类问题。

刚刚在我们的一个组件中替换了这段代码

this.setState(prevState => ({
   ...prevState,
        preferences: {
            ...prevState.preferences,
            [key]: newValue
        }
}));

有了这个

import produce from 'immer';

this.setState(produce(draft => {
    draft.preferences[key] = newValue;
}));

使用 immer,您可以将状态作为“普通对象”处理。魔术发生在代理对象的幕后。


t
tokland

有很多图书馆可以帮助解决这个问题。例如,使用 immutability-helper

import update from 'immutability-helper';

const newState = update(this.state, {
  someProperty: {flag: {$set: false}},
};
this.setState(newState);

使用 lodash/fp 集:

import {set} from 'lodash/fp';

const newState = set(["someProperty", "flag"], false, this.state);

使用 lodash/fp 合并:

import {merge} from 'lodash/fp';

const newState = merge(this.state, {
  someProperty: {flag: false},
});

如果您使用 lodash,您可能想尝试 _.cloneDeep 并将状态设置为克隆副本。
@YixingLiu:我已经更新了使用 lodash/fp 的答案,所以我们不必担心突变。
除了语法之外,在 lodash/fp 中选择 mergeset 函数有什么好处吗?
好的答案,也许您想补充一点,您还必须为 lodash/fp 方法调用 this.setState(newState)。另一个问题是 lodash .set (non-fp) 有不同的参数顺序,这让我有一段时间感到困惑。
Merge 有一些奇怪的行为,当调用 this.setState(newState) 时,它会在 this.state 中创建另一个名为 newState 的状态项。知道为什么吗?
A
Andrew

尽管您询问了基于类的 React 组件的状态,但 useState 挂钩也存在同样的问题。更糟糕的是:useState 挂钩不接受部分更新。因此,当引入 useState 挂钩时,这个问题变得非常重要。

我决定发布以下答案,以确保问题涵盖使用 useState 挂钩的更现代场景:

如果你有:

const [state, setState] = useState({ someProperty: { flag: true, otherNestedProp: 1 }, otherProp: 2 })

您可以通过克隆当前数据并修补所需的数据段来设置嵌套属性,例如:

setState(current => { ...current, someProperty: { ...current.someProperty, flag: false } });

或者您可以使用 Immer 库来简化对象的克隆和修补。

或者您可以使用 Hookstate library(免责声明:我是作者)完全简单地管理复杂(本地和全局)状态数据并提高性能(阅读:不用担心渲染优化):

import { useStateLink } from '@hookstate/core' 
const state = useStateLink({ someProperty: { flag: true, otherNestedProp: 1 }, otherProp: 2 })

获取要渲染的字段:

state.nested.someProperty.nested.flag.get()
// or 
state.get().someProperty.flag

设置嵌套字段:

state.nested.someProperty.nested.flag.set(false)

这是 Hookstate 示例,其中状态深度/递归嵌套在 tree-like data structure 中。


M
Matthew Berkompas

这是此线程中给出的第一个答案的变体,不需要任何额外的包、库或特殊功能。

state = {
  someProperty: {
    flag: 'string'
  }
}

handleChange = (value) => {
  const newState = {...this.state.someProperty, flag: value}
  this.setState({ someProperty: newState })
}

为了设置特定嵌套字段的状态,您已经设置了整个对象。我通过创建一个变量 newState 并使用 ES2015 spread operator首先将当前状态的内容传播到其中来做到这一点。然后,我将 this.state.flag 的值替换为新值(由于我设置 flag: value 之后 将当前状态传播到对象中,当前状态中的 flag 字段被覆盖) .然后,我只需将 someProperty 的状态设置为我的 newState 对象。


A
Alberto Piras

我使用了这个解决方案。

如果您有这样的嵌套状态:

   this.state = {
          formInputs:{
            friendName:{
              value:'',
              isValid:false,
              errorMsg:''
            },
            friendEmail:{
              value:'',
              isValid:false,
              errorMsg:''
            }
}

您可以声明复制当前状态并使用更改的值重新分配它的 handleChange 函数

handleChange(el) {
    let inputName = el.target.name;
    let inputValue = el.target.value;

    let statusCopy = Object.assign({}, this.state);
    statusCopy.formInputs[inputName].value = inputValue;

    this.setState(statusCopy);
  }

这里是带有事件监听器的 html

<input type="text" onChange={this.handleChange} " name="friendName" />

确认的。如果您已经在处理已经建立的具有嵌套属性的项目,这将非常有用。
Object.assign 只做一个浅拷贝,所以你正在改变一个嵌套对象。不要改变以前的状态。
u
user2208124

尽管嵌套并不是你真正应该如何对待组件状态的方式,但有时对于单层嵌套来说很容易。

对于这样的状态

state = {
 contact: {
  phone: '888-888-8888',
  email: 'test@test.com'
 }
 address: {
  street:''
 },
 occupation: {
 }
}

我使用的可重复使用的方法看起来像这样。

handleChange = (obj) => e => {
  let x = this.state[obj];
  x[e.target.name] = e.target.value;
  this.setState({ [obj]: x });
};

然后只需为您要处理的每个嵌套传递 obj 名称...

<TextField
 name="street"
 onChange={handleChange('address')}
 />

这会改变当前状态对象而不是创建副本。不要改变状态!
r
ravibagul91

创建状态的副本:

let someProperty = JSON.parse(JSON.stringify(this.state.someProperty))

对此对象进行更改:

someProperty.flag = "false"

现在更新状态

this.setState({someProperty})

setState 是异步的。所以当它更新时,其他人可以调用这个函数并复制一个过时的状态版本。
K
Ken

根据框架的标准,不确定这在技术上是否正确,但有时您只需要更新嵌套对象。这是我使用钩子的解决方案。

setInputState({ ...inputState, [parentKey]: { ...inputState[parentKey], [childKey]: value }, });


你实施了吗?
m
moshfiqrony

我看到每个人都给出了基于类的组件状态更新解决方案,这是预期的,因为他要求这样做,但我试图为钩子提供相同的解决方案。

const [state, setState] = useState({
    state1: false,
    state2: 'lorem ipsum'
})

现在,如果您只想更改嵌套对象键 state1,则可以执行以下任何操作:

过程 1

let oldState = state;
oldState.state1 = true
setState({...oldState);

过程 2

setState(prevState => ({
    ...prevState,
    state1: true
}))

我最喜欢过程2。


“进程 1”是 React 中的反模式,因为它会改变当前状态对象。
C
Cory House

尚未提及的另外两个选项:

如果您有深度嵌套的状态,请考虑是否可以将子对象重组为位于根部。这使得数据更容易更新。有许多方便的库可用于处理 Redux 文档中列出的不可变状态。我推荐 Immer,因为它允许您以可变方式编写代码,但在幕后处理必要的克隆。它还会冻结生成的对象,因此您以后不会意外地对其进行变异。


V
Venugopal

为了使事情通用,我研究了@ShubhamKhatri 和@Qwerty 的答案。

状态对象

this.state = {
  name: '',
  grandParent: {
    parent1: {
      child: ''
    },
    parent2: {
      child: ''
    }
  }
};

输入控件

<input
  value={this.state.name}
  onChange={this.updateState}
  type="text"
  name="name"
/>
<input
  value={this.state.grandParent.parent1.child}
  onChange={this.updateState}
  type="text"
  name="grandParent.parent1.child"
/>
<input
  value={this.state.grandParent.parent2.child}
  onChange={this.updateState}
  type="text"
  name="grandParent.parent2.child"
/>

更新状态方法

setState 作为@ShubhamKhatri 的回答

updateState(event) {
  const path = event.target.name.split('.');
  const depth = path.length;
  const oldstate = this.state;
  const newstate = { ...oldstate };
  let newStateLevel = newstate;
  let oldStateLevel = oldstate;

  for (let i = 0; i < depth; i += 1) {
    if (i === depth - 1) {
      newStateLevel[path[i]] = event.target.value;
    } else {
      newStateLevel[path[i]] = { ...oldStateLevel[path[i]] };
      oldStateLevel = oldStateLevel[path[i]];
      newStateLevel = newStateLevel[path[i]];
    }
  }
  this.setState(newstate);
}

setState 作为@Qwerty 的回答

updateState(event) {
  const path = event.target.name.split('.');
  const depth = path.length;
  const state = { ...this.state };
  let ref = state;
  for (let i = 0; i < depth; i += 1) {
    if (i === depth - 1) {
      ref[path[i]] = event.target.value;
    } else {
      ref = ref[path[i]];
    }
  }
  this.setState(state);
}

注意:以上这些方法不适用于数组


S
Stephen Paul

我非常重视有关创建组件状态的完整副本的问题already voiced。话虽如此,我强烈建议Immer

import produce from 'immer';

<Input
  value={this.state.form.username}
  onChange={e => produce(this.state, s => { s.form.username = e.target.value }) } />

这应该适用于 React.PureComponent(即 React 的浅层状态比较),因为 Immer 巧妙地使用代理对象来有效地复制任意深度的状态树。与 Immutability Helper 等库相比,Immer 的类型安全性更高,非常适合 Javascript 和 Typescript 用户。

打字稿实用功能

function setStateDeep<S>(comp: React.Component<any, S, any>, fn: (s: 
Draft<Readonly<S>>) => any) {
  comp.setState(produce(comp.state, s => { fn(s); }))
}

onChange={e => setStateDeep(this, s => s.form.username = e.target.value)}

R
R Rejneesh Pillai

如果要动态设置状态

以下示例动态设置表单状态,其中状态中的每个键都是对象

 onChange(e:React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) {
    this.setState({ [e.target.name]: { ...this.state[e.target.name], value: e.target.value } });
  }

M
Michael

我发现这对我有用,在我的情况下有一个项目表单,例如你有一个 id 和一个名称,我宁愿维护嵌套项目的状态。

return (
  <div>
      <h2>Project Details</h2>
      <form>
        <Input label="ID" group type="number" value={this.state.project.id} onChange={(event) => this.setState({ project: {...this.state.project, id: event.target.value}})} />
        <Input label="Name" group type="text" value={this.state.project.name} onChange={(event) => this.setState({ project: {...this.state.project, name: event.target.value}})} />
      </form> 
  </div>
)

让我知道!


b
barbsan
stateUpdate = () => {
    let obj = this.state;
    if(this.props.v12_data.values.email) {
      obj.obj_v12.Customer.EmailAddress = this.props.v12_data.values.email
    }
    this.setState(obj)
}

我认为这是不正确的,因为 obj 只是对状态的引用,所以通过它你正在改变实际的 this.state 对象
D
DragonKnight

这显然不是正确或最好的方法,但在我看来它更清晰:

this.state.hugeNestedObject = hugeNestedObject; 
this.state.anotherHugeNestedObject = anotherHugeNestedObject; 

this.setState({})

然而,React 本身应该迭代思想嵌套对象并相应地更新状态和 DOM,而这还不存在。


不要改变当前状态,这是 React 中的反模式。
s
shavina

将此用于多个输入控件和动态嵌套名称

<input type="text" name="title" placeholder="add title" onChange={this.handleInputChange} />
<input type="checkbox" name="chkusein" onChange={this.handleInputChange} />
<textarea name="body" id="" cols="30" rows="10" placeholder="add blog content" onChange={this.handleInputChange}></textarea>

代码非常可读

处理程序

handleInputChange = (event) => {
        const target = event.target;
        const value = target.type === 'checkbox' ? target.checked : target.value;
        const name = target.name;
        const newState = { ...this.state.someProperty, [name]: value }
        this.setState({ someProperty: newState })
    }

A
Alex Pandrea

以下是使用 solution in this answer 的嵌套状态(一级)的完整示例,用于实现为类的组件:

class CaveEditModal extends React.Component {

  // ...

  constructor(props, context) {
    super(props);
    this.state = {

      tabValue: '1',
      isModalOpen: this.props.isModalOpen,

      // ...
      caveData: {
        latitude: 1,
        longitude: 2      
      }
    };

    // ... 

    const updateNestedFieldEvent = fullKey => ev => { 
      
      var [parentProperty, _key] = fullKey.split(".", 2);

      this.setState({[parentProperty]: { ...this.state[parentProperty], [_key]: ev.target.value} });
    };
    // ...

    this.handleLatitudeChange = updateNestedFieldEvent('caveData.latitude');
    this.handleLongitudeChange = updateNestedFieldEvent('caveData.longitude');
  }

  render () {    
    return (
      <div>
         <TextField id="latitude" label="Latitude" type="number" value={this.state.caveData.latitude} onChange={this.handleLatitudeChange} />
         <TextField id="longitude" label="Longitude" type="number" value={this.state.caveData.longitude} onChange={this.handleLongitudeChange} />
         <span>lat={this.state.caveData.latitude} long={this.state.caveData.longitude}</span>
      </div>
    );
  };

}

请注意,状态更新器函数 updateNestedFieldEvent 仅适用于像 a.b 这样的一级嵌套对象,而不像 a.b.c


E
Eladian

这样的事情可能就足够了,

const isObject = (thing) => {
    if(thing && 
        typeof thing === 'object' &&
        typeof thing !== null
        && !(Array.isArray(thing))
    ){
        return true;
    }
    return false;
}

/*
  Call with an array containing the path to the property you want to access
  And the current component/redux state.

  For example if we want to update `hello` within the following obj
  const obj = {
     somePrimitive:false,
     someNestedObj:{
        hello:1
     }
  }

  we would do :
  //clone the object
  const cloned = clone(['someNestedObj','hello'],obj)
  //Set the new value
  cloned.someNestedObj.hello = 5;

*/
const clone = (arr, state) => {
    let clonedObj = {...state}
    const originalObj = clonedObj;
    arr.forEach(property => {
        if(!(property in clonedObj)){
            throw new Error('State missing property')
        }

        if(isObject(clonedObj[property])){
            clonedObj[property] = {...originalObj[property]};
            clonedObj = clonedObj[property];
        }
    })
    return originalObj;
}

const nestedObj = {
    someProperty:true,
    someNestedObj:{
        someOtherProperty:true
    }
}

const clonedObj = clone(['someProperty'], nestedObj);
console.log(clonedObj === nestedObj) //returns false
console.log(clonedObj.someProperty === nestedObj.someProperty) //returns true
console.log(clonedObj.someNestedObj === nestedObj.someNestedObj) //returns true

console.log()
const clonedObj2 = clone(['someProperty','someNestedObj','someOtherProperty'], nestedObj);
console.log(clonedObj2 === nestedObj) // returns false
console.log(clonedObj2.someNestedObj === nestedObj.someNestedObj) //returns false
//returns true (doesn't attempt to clone because its primitive type)
console.log(clonedObj2.someNestedObj.someOtherProperty === nestedObj.someNestedObj.someOtherProperty) 

E
Elnoor

我知道这是一个老问题,但仍然想分享我是如何做到这一点的。假设构造函数中的状态如下所示:

  constructor(props) {
    super(props);

    this.state = {
      loading: false,
      user: {
        email: ""
      },
      organization: {
        name: ""
      }
    };

    this.handleChange = this.handleChange.bind(this);
  }

我的 handleChange 函数是这样的:

  handleChange(e) {
    const names = e.target.name.split(".");
    const value = e.target.type === "checkbox" ? e.target.checked : e.target.value;
    this.setState((state) => {
      state[names[0]][names[1]] = value;
      return {[names[0]]: state[names[0]]};
    });
  }

并确保您相应地命名输入:

<input
   type="text"
   name="user.email"
   onChange={this.handleChange}
   value={this.state.user.firstName}
   placeholder="Email Address"
/>

<input
   type="text"
   name="organization.name"
   onChange={this.handleChange}
   value={this.state.organization.name}
   placeholder="Organization Name"
/>

state[names[0]][names[1]] = value 这会改变之前的状态对象,这是 React 的反模式。
E
Emisael Carrera

我使用减少搜索进行嵌套更新:

例子:

状态中的嵌套变量:

state = {
    coords: {
        x: 0,
        y: 0,
        z: 0
    }
}

功能:

handleChange = nestedAttr => event => {
  const { target: { value } } = event;
  const attrs = nestedAttr.split('.');

  let stateVar = this.state[attrs[0]];
  if(attrs.length>1)
    attrs.reduce((a,b,index,arr)=>{
      if(index==arr.length-1)
        a[b] = value;
      else if(a[b]!=null)
        return a[b]
      else
        return a;
    },stateVar);
  else
    stateVar = value;

  this.setState({[attrs[0]]: stateVar})
}

利用:

<input
value={this.state.coords.x}
onChange={this.handleTextChange('coords.x')}
/>

a[b] = value 这会改变 this.state[attrs[0]] 返回的嵌套状态对象。不要改变当前状态对象。
R
Ramiro Carbonell Delgado

这是我的初始状态

    const initialStateInput = {
        cabeceraFamilia: {
            familia: '',
            direccion: '',
            telefonos: '',
            email: ''
        },
        motivoConsulta: '',
        fechaHora: '',
        corresponsables: [],
    }

钩子或者你可以用状态(类组件)替换它

const [infoAgendamiento, setInfoAgendamiento] = useState(initialStateInput);

handleChange 的方法

const actualizarState = e => {
    const nameObjects = e.target.name.split('.');
    const newState = setStateNested(infoAgendamiento, nameObjects, e.target.value);
    setInfoAgendamiento({...newState});
};

使用嵌套状态设置状态的方法

const setStateNested = (state, nameObjects, value) => {
    let i = 0;
    let operativeState = state;
    if(nameObjects.length > 1){
        for (i = 0; i < nameObjects.length - 1; i++) {
            operativeState = operativeState[nameObjects[i]];
        }
    }
    operativeState[nameObjects[i]] = value;
    return state;
}

最后这是我使用的输入

<input type="text" className="form-control" name="cabeceraFamilia.direccion" placeholder="Dirección" defaultValue={infoAgendamiento.cabeceraFamilia.direccion} onChange={actualizarState} />

S
Sharad Sharma

如果你在你的项目中使用 formik,它有一些简单的方法来处理这些东西。这是使用 formik 最简单的方法。

首先在 formik initivalues 属性或反应中设置初始值。状态

这里,初始值是在反应状态中定义的

   state = { 
     data: {
        fy: {
            active: "N"
        }
     }
   }

为 formik initiValues 属性内的 formik 字段定义上述初始值

<Formik
 initialValues={this.state.data}
 onSubmit={(values, actions)=> {...your actions goes here}}
>
{({ isSubmitting }) => (
  <Form>
    <Field type="checkbox" name="fy.active" onChange={(e) => {
      const value = e.target.checked;
      if(value) setFieldValue('fy.active', 'Y')
      else setFieldValue('fy.active', 'N')
    }}/>
  </Form>
)}
</Formik>

制作一个控制台来检查更新到 string 的状态,而不是 booleanformik setFieldValue 函数来设置状态或使用 react 调试器工具来查看 formik 状态值的变化。


L
LoneWanderer

试试这个代码:

this.setState({ someProperty: {flag: false} });

但这将删除 someProperty 引用的对象中存在的任何其他属性,并且在执行上述代码后,仅 flag 属性将保留