简体   繁体   English

React Context初始状态正在变异--JavaScript / React

[英]React Context initial state is being mutated - JavaScript / React

I am making a guide me section. 我正在制作一个指导我部分。 What this guide me section does - displays an array of processes. 本指南的部分内容 - 显示一系列流程。 Within each process is an array of steps, within each step is an array of options. 每个进程内都有一个步骤数组,每个步骤中都有一个选项数组。 The user selects an option from one of the steps, it takes them to the next correlating step. 用户从其中一个步骤中选择一个选项,将它们带到下一个相关步骤。 If the user selects the option on step 2, it could take them to step 3 or back to step 1. It depends on the id. 如果用户在步骤2中选择了该选项,则可以将它们带到步骤3或返回步骤1.这取决于id。

With all that said I'm having issues with my Process mutating on me. 尽管如此,我的过程中有一些问题在改变我。 I'm using React Context as a global state. 我正在使用React Context作为全局状态。 When a user selects an option, I'm grabbing that id, then filtering the designated object by that id. 当用户选择一个选项时,我抓住那个id,然后按该id过滤指定的对象。 So I should only be left is that processes with that specific step. 所以我只能留下具有该特定步骤的流程。 What's happening is my initial global state is mutating somehow. 发生的事情是我最初的全球状态正以某种方式发生变异。 I'm missing something here as I'm new to React. 我在这里错过了一些东西,因为我是React的新手。

Ps - I'm not using any services at this moment, so I just copied some JSON over to my initial state in context.js Ps - 我现在没有使用任何服务,所以我只是将一些JSON复制到context.js中的初始状态

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 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 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

Guide-Steps.js 导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

On change in GuideSteps is where I'm doing the logic for filtering and setting up my new object. 在GuideSteps中进行更改是我在进行过滤和设置新对象的逻辑。

EDIT 编辑

This fixed the issue but I think it's too expensive. 这解决了这个问题,但我认为这太贵了。 How would I go about solving this issue without having to reparse the array. 如何在不重新解析数组的情况下解决此问题。

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

So it looks like you're mutating initialGuide via reference. 所以看起来你正在通过引用改变initialGuide

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM