【问题标题】:Use of React getDerivedStateFromProps()React getDerivedStateFromProps() 的使用
【发布时间】:2019-10-20 12:32:21
【问题描述】:

我正在探索 React,对生命周期方法和父子通信有些困惑。具体来说,我正在尝试创建一个组件,该组件包装一个选择元素并在选择“其他”选项时添加一个输入框。我已经使用getDerivedStateFromProps() 实现了这个,但根据documentation,这个生命周期方法应该很少使用。因此我的问题是:在这种情况下我应该注意和使用另一种模式吗?

这是我的代码,值和选项作为 props 向下传递,父组件的 handleChange() 方法也是如此。所以当 select 或 input 元素发生变化时,首先更新父组件状态,并通过props.value向下传递一个新值。

export default class SelectOther extends Component {
    constructor(props) {
        super(props);
        this.handleChange = this.handleChange.bind(this);
    }

    static getDerivedStateFromProps(props) {
        let optionIndex = -1;
        for (let i = 0; i < props.options.length; i++) {
            if (props.options[i].value === props.value) {
                optionIndex = i;
                break;
            }
        }
        if (optionIndex > -1) {
            return {
                selected: props.options[optionIndex].value,
                text: "",
                showInput: false
            };
        } else {
            return {
                selected: "",
                text: props.value,
                showInput: true
            };
        }
    }

    handleChange(e) {
        this.props.handleChange({
            "target": {
                "name": this.props.name,
                "value": e.target.value
            }
        });
    }

    render() {
        return (
            <div>
                <label>{ this.props.label }</label>
                <select name={ this.props.name } value={ this.state.selected } onChange={ this.handleChange }>
                    {
                        this.props.options.map(option => <option key={option.value} value={option.value}>{option.label}</option>)
                    }
                    <option value="">Other</option>
                </select>
                {
                    this.state.showInput &&
                        <div>
                            <label>{ this.props.label } (specify other)</label>
                            <input type="text" className="form-control" value={ this.state.text } onChange={ this.handleChange }></input>
                        </div>
                }
            </div>
        )
    }
}

【问题讨论】:

    标签: javascript reactjs


    【解决方案1】:

    您可以通过不让 SelectOther 具有任何状态来简化操作,这里是一个示例,说明如何传递一个分派操作以更改值的函数。因为 SelectOther 是一个纯组件,它不会不必要地重新渲染:

    //make this a pure component so it won't re render
    const SelectOther = React.memo(function SelectOther({
      label,
      name,
      value,
      options,
      handleChange,
    }) {
      console.log('in render',name, value);
      const showInput = !options
        .map(o => o.value)
        .includes(value);
      return (
        <div>
          <label>{label}</label>
          <select
            name={name}
            value={showInput ? '' : value}
            onChange={handleChange}
          >
            {options.map(option => (
              <option key={option.value} value={option.value}>
                {option.label}
              </option>
            ))}
            <option value="">Other</option>
          </select>
          {showInput && (
            <div>
              <label>{label} (specify other)</label>
              <input
                type="text"
                name={name}
                className="form-control"
                value={value}
                onChange={handleChange}
              ></input>
            </div>
          )}
        </div>
      );
    });
    
    const App = () => {
      //create options once during App life cycle
      const options = React.useMemo(
        () => [
          { value: 'one', label: 'one label' },
          { value: 'two', label: 'two label' },
        ],
        []
      );
      //create a state to hold input values and provide
      //  a reducer to create new state based on actions
      const [state, dispatch] = React.useReducer(
        (state, { type, payload }) => {
          //if type of action is change then change the
          //  payload.name field to payload.value
          if (type === 'CHANGE') {
            const { name, value } = payload;
            return { ...state, [name]: value };
          }
          return state;
        },
        //initial state for the inputs
        {
          my_name: '',
          other_input: options[0].value,
        }
      );
      //use React.useCallback to create a callback
      //  function that doesn't change. This would be
      //  harder if you used useState instead of useReducer
      const handleChange = React.useCallback(
        ({ target: { name, value } }) => {
          dispatch({
            type: 'CHANGE',
            payload: {
              name,
              value,
            },
          });
        },
        []
      );
      return (
        <div>
          <SelectOther
            label="label"
            name="my_name"
            value={state.my_name}
            options={options}
            handleChange={handleChange}
          />
          <SelectOther
            label="other"
            name="other_input"
            value={state.other_input}
            options={options}
            handleChange={handleChange}
          />
        </div>
      );
    };
    
    
    //render app
    ReactDOM.render(
      <App />,
      document.getElementById('root')
    );
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
    <div id="root"></div>

    我可以在 App 中使用 useState,但我必须使用 useEventCallback 或对每个输入值执行 useState。 following documentation 提出了 useEventCallback 模式,然后紧接着声明我们不推荐这种模式,所以这就是我提出 useReducer 解决方案的原因。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-05-07
      • 2018-10-11
      • 1970-01-01
      • 2020-12-30
      • 2023-02-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-09
      相关资源
      最近更新 更多