从表单构建器中深层嵌套的子项自下而上地更新状态

React Updating state bottom up from deeply nested children in a form builder

我需要帮助思考从深度嵌套 children/grandchildren 更新父状态的最佳方法。

我正在使用 React 创建一个表单生成器,它允许用户通过在 localeStorage 中保存持久状态来创建、预览和导出表单。一个表格有问题,每个问题都有一个可能的子问题。

我的想法是在app层保持所有问题的状态,赋值localStorage.setItem('questions',this.state.questions)

数据结构如下:

     [
      { question='question 1', 
        type='text', 
        condition=null, 
        isSub= false, 
        subQ=[{
            question='sub question 1',
            ...
            isSub=true,
            subQ=[]
          }]
        },
        {
          question='question 1', 
          ...
          isSub= false, 
          subQ=[]
        },
        question='question 1', 
        ...
        isSub= false, 
        subQ=[]
      }
    ]

我的Component层级结构是App -> QuestionForm -> Question。 App 保持所有问题的状态,Question 管理自己的 onChange 调用 updateInputValue 更新值,然后调用绑定到 QuestionForm 组件的 updateParent(key, update)。 QuestionForm 然后调用绑定到 App 的 updateQuestions(update)。这适用于最外层的问题。使用对子问题所做的更改来更新 App 状态的最佳方法是什么?

   class Question extends Component{
      constructor(props){
        super(props)
        this.state = {
          question: props.question
        }
        this.updateInputValue = this.updateInputValue.bind(this)
      }
      .....
      updateInputValue(e){
        let { name, value } = e.target, 
            question = {...this.props.question }
        if( !name ){
          value = e.target.parentElement.getAttribute('name') || 
                  e.target.getAttribute('name')
          name = 'type'
        } 
        question[name] = value
        this.setState({question: question})
        this.props.updateParent(this.props.id, question)
      }

这就是我在问题中呈现每个问题的子问题的方式 渲染()组件

{ question.subQ.map( (q, i) => 
                  <Question key={i} 
                           question={q} 
                        updateParent={this.props.updateParent} /> ) }

QuestionForm组件内部的updateParent函数

      updateParent = (key, prop) => {
        let questions = {...this.state.questions }
        questions[key] = prop
        this.props.updateQuestions(questions)
      }

最后,App组件

          class App extends Component {
        constructor(props){
          super(props)
          this.state = {
            questions: sample,
            activeItem: 'Create'
          }
          this.checkStorage = checkStorage
          this.addQuestion = addQuestion.bind(this)
          this.handleTabClick = this.handleTabClick.bind(this)
          this.updateQuestions = this.updateQuestions.bind(this)
        }

        updateQuestions(update){
          this.setState({questions: update})
        }

我正在考虑一种自下而上的方法,通过跟踪 parentKeys 并爬上树直到 'isSub' === false。有没有更好的方法来解决这个问题?

如果 App 要存储您的问题状态,我认为最简单的方法就是为 App 提供一个 updateQuestion 方法,如下所示:

updateQuestion(path, value) {
  const { questions } = this.state;
  _.set(questions, path, value);
  this.setState({ questions });
}

在这里,我们使用 lodash's set method 来遍历数据结构并更改您需要的任何值。现在,我们可以绑定这个函数并将其作为 prop 传递给每个问题,并根据需要传递给每个子问题。

然后,在您的渲染方法中,当您将每个问题映射到您的数据数组时,动态地为其分配一个 path 道具,该道具反映您将在您的状态中更改的值的位置该特定问题调用 updateQuestion。

将应用程序的 state.questions(或其一部分)作为道具传递给每个问题,您可以避免让它们保持自己的状态,这意味着除非您需要生命周期挂钩,否则您可以将问题变成一个功能组件。

不过,老实说,当您发现自己不得不像这样冒泡状态时,是时候问问自己 redux 是否不合适了。如果您以前没有使用过它,您将有一些初始增长的主电源,但它会为您节省很多精力和不必要的复杂性。