React 上下文初始状态正在发生变化 - JavaScript / React
React Context initial state is being mutated - JavaScript / React
我正在做一个引导我的部分。本指南部分的作用 - 显示一系列流程。每个过程中都有一系列步骤,每个步骤中都有一系列选项。用户从其中一个步骤中选择一个选项,将他们带到下一个相关步骤。如果用户在第 2 步选择了选项,则可能会将他们带到第 3 步或返回到第 1 步。这取决于 ID。
综上所述,我遇到了我的流程变异问题。我正在使用 React Context 作为全局状态。当用户选择一个选项时,我会获取该 ID,然后按该 ID 过滤指定的对象。所以我应该只剩下那个特定步骤的过程。发生的事情是我的初始全局状态正在以某种方式发生变化。由于我是 React 的新手,所以我在这里遗漏了一些东西。
P.s - 我目前没有使用任何服务,所以我只是复制了一些 JSON 到 context.js
中的初始状态
context.js
import React, { Component } from 'react'
// import axios from 'axios'
const Context = React.createContext()
const reducer = (state, action) => {
switch(action.type){
case 'SEARCH_PROCESS':
return {
...state,
guides: action.payload
}
default:
return state
}
}
export class Provider extends Component {
state = {
guides: [
{
"processName": "Support Messaging",
"steps": [{
"id": "15869594739302",
"title": "step one",
"options": [{
"nextStep": "4767fn-47587n-2819am-9585j,04956840987",
"text": "Option 1 text",
"type": "option"
},
{
"nextStep": "4767fn-47587n-2819am-9585j,04956840987",
"text": "Option 2 text",
"type": "option"
},
{
"nextStep": "",
"text": "Option 3 text",
"type": "option"
}
]
},
{
"id": "04956840987",
"title": "step two",
"options": [{
"nextStep": "4767fn-47587n-2819am-9585j,15869594739302",
"text": "Return to step1",
"type": "option"
},
{
"nextStep": "",
"text": "Option 2 text",
"type": "option"
},
{
"nextStep": "",
"text": "Option 3 text",
"type": "option"
}
]
}
],
"owner": "bob",
"id": "4767fn-47587n-2819am-9585j",
"lastUpdated": 154222227099000,
"tags": ["Tag1", "Tag2", "Tag3"]
}
],
"owner": "bob",
"id": "4767fn-47587n-2819am-9585x",
"lastUpdated": 154222227099000,
"tags": ["Tag1", "Tag2", "Tag3"]
}
],
initialGuide: [
{
"processName": "Support Messaging",
"steps": [{
"id": "15869594739302",
"title": "step one",
"options": [{
"nextStep": "4767fn-47587n-2819am-9585j,04956840987",
"text": "Option 1 text",
"type": "option"
},
{
"nextStep": "4767fn-47587n-2819am-9585j,04956840987",
"text": "Option 2 text",
"type": "option"
},
{
"nextStep": "",
"text": "Option 3 text",
"type": "option"
}
]
},
{
"id": "04956840987",
"title": "step two",
"options": [{
"nextStep": "4767fn-47587n-2819am-9585j,15869594739302",
"text": "Return to step1",
"type": "option"
},
{
"nextStep": "",
"text": "Option 2 text",
"type": "option"
},
{
"nextStep": "",
"text": "Option 3 text",
"type": "option"
}
]
}
],
"owner": "bob",
"id": "4767fn-47587n-2819am-9585j",
"lastUpdated": 154222227099000,
"tags": ["Tag1", "Tag2", "Tag3"]
}
],
dispatch: action => this.setState(state => reducer(state, action))
}
render() {
return (
<Context.Provider value={this.state}>
{this.props.children}
</Context.Provider>
)
}
}
export const Consumer = Context.Consumer;
Guides.js
import React, { Component } from 'react'
import { Consumer } from '../../context'
import Process from './Process'
class Guides extends Component {
constructor (props) {
super(props)
this.state = {
contextValue: [],
searchData: props.location.data
}
}
render () {
console.log(this.props.location.data, this.state, 'logging state and props on guides')
// this.state.searchData = this.props.location.data
return (
<Consumer>
{value => {
return (
<React.Fragment>
<div className="content-wrapper">
<h1>Guide Me</h1>
<div className="ms-Grid times--max-width" dir="ltr">
<div className="ms-Grid-row">
<div className="profile--wrapper ms-Grid-col ms-sm12 ms-md12 ms-lg5">
{value.guides.map(item => {
return <Guide key={item.id} guide={item} processValue={value.guides} initialGuide={value.initialGuide}/>
})}
</div>
</div>
</div>
</div>
</React.Fragment>
)
}}
</Consumer>
)
}
}
export default Guides
Process.js
import React, { Component } from 'react'
import GuideSteps from './Guide-Steps'
import { Consumer } from '../../context'
class Process extends Component {
constructor(props) {
super(props)
this.state = {
processName: this.props.guide.processName,
process: this.props.guide,
steps: this.props.guide.steps,
selectedIndex: 0,
selectedStep: '',
processValue: this.props.processValue,
initialGuide: this.props.initialGuide
}
this.displayStep = this.displayStep.bind(this)
}
displayStep = (res, dispatch) => {
this.setState({ selectedStep: res })
}
render() {
const { steps, selectedIndex, process, processName, processValue, initialGuide } = this.state
return (
<Consumer>
{value => {
return (
<div>
<h2 className="profile--sub-header--bold">{processName}</h2>
<GuideSteps
key={this.props.guide.steps[selectedIndex].id}
selectedStep={this.props.guide.steps[selectedIndex]}
stepValue={this.displayStep}
process={process}
processValue={processValue}
initialGuide={initialGuide}
/>
</div>
)
}}
</Consumer>
)
}
}
export default Process
指南-Steps.js
import React, { Component } from 'react'
import { ChoiceGroup } from 'office-ui-fabric-react/lib/ChoiceGroup'
import { Consumer } from '../../context'
class GuideSteps extends Component {
constructor(props) {
super(props);
this.state = {
process: [],
selectedStep: this.props.selectedStep,
dispatch: '',
processValue: this.props.processValue,
initialGuide: ''
}
this._onChange = this._onChange.bind(this)
}
_onChange = (ev, option) => {
// this.props.stepValue(option.value.nextStep)
const { dispatch , initialGuide } = this.state
let optionArray = option.value.nextStep.split(',')
let processArray = this.state.process.filter(item => {
return item.id === optionArray[0]
})
let stepArray = processArray[0].steps.filter(item => {
return item.id === optionArray[1]
})
console.log(stepArray, processArray, this.state.process, 'logging step array before setting')
processArray[0].steps = stepArray
console.log(stepArray, processArray, this.state.process, 'logging step array after setting')
dispatch({
type: 'SEARCH_PROCESS',
payload: processArray
})
}
render() {
let options = []
{
this.props.selectedStep.options.map(item => {
return options.push({
key: item.text,
text: item.text,
value: item
})
})
}
return (
<Consumer>
{value => {
const { dispatch, guides, initialGuide } = value
this.state.dispatch = dispatch
console.log(value, 'logging initial guide in render')
this.state.process = initialGuide
return (
<div>
<ChoiceGroup
className="defaultChoiceGroup"
options={options}
onChange={this._onChange}
/>
</div>
)
}}
</Consumer>
)
}
}
export default GuideSteps
在 GuideSteps 的更改中,我正在执行过滤和设置新对象的逻辑。
编辑
这解决了问题,但我认为它太贵了。我将如何解决这个问题而不必重新解析数组。
update: (ev, option) => {
const { initialGuide } = this.state
if (option.value.nextStep !== null && option.value.nextStep !== '') {
//split string
const optionArray = option.value.nextStep.split(',')
//filter process array
const processArray = initialGuide.filter(process => {
return process.id === optionArray[0]
})
//filter step array
const stepArray = processArray[0].steps.filter(
item => item.id === optionArray[1]
)
if(stepArray.length > 0 && stepArray !== null) {
//get a copy of the process array so original is not mutated by the steps
let stringC = JSON.stringify(processArray)
let stringD = JSON.parse(stringC)
stringD[0].steps = stepArray
//issue might be here visually where setting the state happens quickly, therefore radio button visual does not display in time.
setTimeout(() => {
this.setState({ guides: stringD })
}, 200)
}
}
},
this.state.process = initialGuide
let processArray = this.state.process.filter...
processArray[0].steps = stepArray
所以看起来你正在通过引用改变 initialGuide
。
我正在做一个引导我的部分。本指南部分的作用 - 显示一系列流程。每个过程中都有一系列步骤,每个步骤中都有一系列选项。用户从其中一个步骤中选择一个选项,将他们带到下一个相关步骤。如果用户在第 2 步选择了选项,则可能会将他们带到第 3 步或返回到第 1 步。这取决于 ID。
综上所述,我遇到了我的流程变异问题。我正在使用 React Context 作为全局状态。当用户选择一个选项时,我会获取该 ID,然后按该 ID 过滤指定的对象。所以我应该只剩下那个特定步骤的过程。发生的事情是我的初始全局状态正在以某种方式发生变化。由于我是 React 的新手,所以我在这里遗漏了一些东西。
P.s - 我目前没有使用任何服务,所以我只是复制了一些 JSON 到 context.js
中的初始状态context.js
import React, { Component } from 'react'
// import axios from 'axios'
const Context = React.createContext()
const reducer = (state, action) => {
switch(action.type){
case 'SEARCH_PROCESS':
return {
...state,
guides: action.payload
}
default:
return state
}
}
export class Provider extends Component {
state = {
guides: [
{
"processName": "Support Messaging",
"steps": [{
"id": "15869594739302",
"title": "step one",
"options": [{
"nextStep": "4767fn-47587n-2819am-9585j,04956840987",
"text": "Option 1 text",
"type": "option"
},
{
"nextStep": "4767fn-47587n-2819am-9585j,04956840987",
"text": "Option 2 text",
"type": "option"
},
{
"nextStep": "",
"text": "Option 3 text",
"type": "option"
}
]
},
{
"id": "04956840987",
"title": "step two",
"options": [{
"nextStep": "4767fn-47587n-2819am-9585j,15869594739302",
"text": "Return to step1",
"type": "option"
},
{
"nextStep": "",
"text": "Option 2 text",
"type": "option"
},
{
"nextStep": "",
"text": "Option 3 text",
"type": "option"
}
]
}
],
"owner": "bob",
"id": "4767fn-47587n-2819am-9585j",
"lastUpdated": 154222227099000,
"tags": ["Tag1", "Tag2", "Tag3"]
}
],
"owner": "bob",
"id": "4767fn-47587n-2819am-9585x",
"lastUpdated": 154222227099000,
"tags": ["Tag1", "Tag2", "Tag3"]
}
],
initialGuide: [
{
"processName": "Support Messaging",
"steps": [{
"id": "15869594739302",
"title": "step one",
"options": [{
"nextStep": "4767fn-47587n-2819am-9585j,04956840987",
"text": "Option 1 text",
"type": "option"
},
{
"nextStep": "4767fn-47587n-2819am-9585j,04956840987",
"text": "Option 2 text",
"type": "option"
},
{
"nextStep": "",
"text": "Option 3 text",
"type": "option"
}
]
},
{
"id": "04956840987",
"title": "step two",
"options": [{
"nextStep": "4767fn-47587n-2819am-9585j,15869594739302",
"text": "Return to step1",
"type": "option"
},
{
"nextStep": "",
"text": "Option 2 text",
"type": "option"
},
{
"nextStep": "",
"text": "Option 3 text",
"type": "option"
}
]
}
],
"owner": "bob",
"id": "4767fn-47587n-2819am-9585j",
"lastUpdated": 154222227099000,
"tags": ["Tag1", "Tag2", "Tag3"]
}
],
dispatch: action => this.setState(state => reducer(state, action))
}
render() {
return (
<Context.Provider value={this.state}>
{this.props.children}
</Context.Provider>
)
}
}
export const Consumer = Context.Consumer;
Guides.js
import React, { Component } from 'react'
import { Consumer } from '../../context'
import Process from './Process'
class Guides extends Component {
constructor (props) {
super(props)
this.state = {
contextValue: [],
searchData: props.location.data
}
}
render () {
console.log(this.props.location.data, this.state, 'logging state and props on guides')
// this.state.searchData = this.props.location.data
return (
<Consumer>
{value => {
return (
<React.Fragment>
<div className="content-wrapper">
<h1>Guide Me</h1>
<div className="ms-Grid times--max-width" dir="ltr">
<div className="ms-Grid-row">
<div className="profile--wrapper ms-Grid-col ms-sm12 ms-md12 ms-lg5">
{value.guides.map(item => {
return <Guide key={item.id} guide={item} processValue={value.guides} initialGuide={value.initialGuide}/>
})}
</div>
</div>
</div>
</div>
</React.Fragment>
)
}}
</Consumer>
)
}
}
export default Guides
Process.js
import React, { Component } from 'react'
import GuideSteps from './Guide-Steps'
import { Consumer } from '../../context'
class Process extends Component {
constructor(props) {
super(props)
this.state = {
processName: this.props.guide.processName,
process: this.props.guide,
steps: this.props.guide.steps,
selectedIndex: 0,
selectedStep: '',
processValue: this.props.processValue,
initialGuide: this.props.initialGuide
}
this.displayStep = this.displayStep.bind(this)
}
displayStep = (res, dispatch) => {
this.setState({ selectedStep: res })
}
render() {
const { steps, selectedIndex, process, processName, processValue, initialGuide } = this.state
return (
<Consumer>
{value => {
return (
<div>
<h2 className="profile--sub-header--bold">{processName}</h2>
<GuideSteps
key={this.props.guide.steps[selectedIndex].id}
selectedStep={this.props.guide.steps[selectedIndex]}
stepValue={this.displayStep}
process={process}
processValue={processValue}
initialGuide={initialGuide}
/>
</div>
)
}}
</Consumer>
)
}
}
export default Process
指南-Steps.js
import React, { Component } from 'react'
import { ChoiceGroup } from 'office-ui-fabric-react/lib/ChoiceGroup'
import { Consumer } from '../../context'
class GuideSteps extends Component {
constructor(props) {
super(props);
this.state = {
process: [],
selectedStep: this.props.selectedStep,
dispatch: '',
processValue: this.props.processValue,
initialGuide: ''
}
this._onChange = this._onChange.bind(this)
}
_onChange = (ev, option) => {
// this.props.stepValue(option.value.nextStep)
const { dispatch , initialGuide } = this.state
let optionArray = option.value.nextStep.split(',')
let processArray = this.state.process.filter(item => {
return item.id === optionArray[0]
})
let stepArray = processArray[0].steps.filter(item => {
return item.id === optionArray[1]
})
console.log(stepArray, processArray, this.state.process, 'logging step array before setting')
processArray[0].steps = stepArray
console.log(stepArray, processArray, this.state.process, 'logging step array after setting')
dispatch({
type: 'SEARCH_PROCESS',
payload: processArray
})
}
render() {
let options = []
{
this.props.selectedStep.options.map(item => {
return options.push({
key: item.text,
text: item.text,
value: item
})
})
}
return (
<Consumer>
{value => {
const { dispatch, guides, initialGuide } = value
this.state.dispatch = dispatch
console.log(value, 'logging initial guide in render')
this.state.process = initialGuide
return (
<div>
<ChoiceGroup
className="defaultChoiceGroup"
options={options}
onChange={this._onChange}
/>
</div>
)
}}
</Consumer>
)
}
}
export default GuideSteps
在 GuideSteps 的更改中,我正在执行过滤和设置新对象的逻辑。
编辑
这解决了问题,但我认为它太贵了。我将如何解决这个问题而不必重新解析数组。
update: (ev, option) => {
const { initialGuide } = this.state
if (option.value.nextStep !== null && option.value.nextStep !== '') {
//split string
const optionArray = option.value.nextStep.split(',')
//filter process array
const processArray = initialGuide.filter(process => {
return process.id === optionArray[0]
})
//filter step array
const stepArray = processArray[0].steps.filter(
item => item.id === optionArray[1]
)
if(stepArray.length > 0 && stepArray !== null) {
//get a copy of the process array so original is not mutated by the steps
let stringC = JSON.stringify(processArray)
let stringD = JSON.parse(stringC)
stringD[0].steps = stepArray
//issue might be here visually where setting the state happens quickly, therefore radio button visual does not display in time.
setTimeout(() => {
this.setState({ guides: stringD })
}, 200)
}
}
},
this.state.process = initialGuide
let processArray = this.state.process.filter...
processArray[0].steps = stepArray
所以看起来你正在通过引用改变 initialGuide
。