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

时间:2018-11-30 03:40:00

标签: javascript reactjs state-management react-context react-state-management

我正在引导我一节。本指南的内容-显示一系列过程。每个过程中包含一系列步骤,每个步骤中包含一系列选项。用户从其中一个步骤中选择一个选项,它将它们带到下一个相关步骤。如果用户在步骤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

Guide-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)
        }
      }
    }, 

1 个答案:

答案 0 :(得分:5)

this.state.process = initialGuide

let processArray = this.state.process.filter...

processArray[0].steps = stepArray

因此,您好像正在通过引用对initialGuide进行突变。