如何在React Hooks中将props更改为state?

38

在简单的React类组件中,我们通常使用以下方式将props更改为state:

constructor(props) {
    super(props)

    this.state = {
      pitch: props.booking.pitch,
      email: props.booking.email,
      firstName: props.booking.firstName,
      arrivalDate: props.booking.arrivalDate
    }
} 

但我不知道如何在新特性Hooks中实现,但我正在尝试这样做。

const GenerateDescHook = ({ description: initialDesc }) => {
  const [description, setDescription] = useState(null)

useEffect(() => {
    setDescription(initialDesc)
  }, {})

 function handleChangeVariance(newVariance) {
    setDescription({
      ...description,
      template: {
        ...description.template,
        variance_name: newVariance,
      },
    })
  }

}

基本上,我只需要将来自另一个父组件的描述属性更改为状态。请问您如何以Hooks的方式展示新的做法?


3
这里的第一个顾虑是您正在将属性复制到本地状态中。这样做的必要性是什么? - mix3d
4个回答

82

你代码中的问题出在{}上。 UseEffect hook需要一个数组作为参数,而不是一个对象。使用[initialDesc]代替。

这是在props改变时重置组件状态的方法

const GenerateDescHook = ({ description: initialDesc }) => {
  const [description, setDescription] = useState(null)

  useEffect(() => {
    setDescription(initialDesc)
  }, [initialDesc]);
}

这是如何在首次渲染时将组件状态初始化为prop值

const GenerateDescHook = ({ description: initialDesc }) => {
  const [description, setDescription] = useState(initialDesc);
}

11
这是正确的处理属性值外部变化的答案。如果没有它,它就无法同步。 - TecHunter
这个问题!我有一个组件,其中pageNumber在外部更改,而useState(props.initialPageNumber)由于某些React隐藏的优化不会使用最新值。尽管这是页面两个不同的组件实例,一个具有initialPageNumber={1},另一个具有initialPageNumber={3} - Danilo Cabello

19

您可以将初始状态作为第一个参数传递给useState,如下所示:

const GenerateDescHook = ({ description: initialDesc }) => {
  const [description, setDescription] = useState(initialDesc)

  ...

4
抱歉,但它没有起到帮助作用。 - Feruza
2
@Feruza,这其实是应该采用的方法,这是钩子组件的对应方式。如果它对你不起作用,可能是你做错了什么。 - Estus Flask
3
需要翻译的内容:It's worth noting that this is only fine if the description prop passed to the GenerateDescHook component never changes and also that the code in the OP has the same drawback. Unless you can guarantee that (which is a dangerous assumption IMO), this is probably the better answer.这段话想表达的意思是,需要注意的是只有在不改变传递给GenerateDescHook组件的description属性的情况下才可以这样做,也要知道OP中的代码存在同样的缺陷。除非你能保证这一点(我认为这是一个危险的假设),否则这个答案可能更好。 - mplis
1
@Feruza 为什么你接受了这个答案?如果父状态改变,子状态将保持不变。我至少是来这里寻找得到更多投票的 useEffect 解决方案的。 - Eric Jones

2
您的状态的初始值是传递给 useState 的值:
const GenerateDescHook = ({ description: initialDesc }) => {
  const [description, setDescription] = useState(initialDesc)

根据文档所述:

function Example() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

这句话的意思是:

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Click me
        </button>
      </div>
    );
  }
}

你如何获取状态对象?而不仅仅是单个属性。 - SuperUberDuper
什么是状态对象?包含您的值的变量是description。而钩子没有任何父变量包含所有状态值。 - Treycos

1

我尝试创建一个钩子函数,以实现依赖于属性值的状态且避免不必要的重新渲染。

以下是我的尝试:

/**
 * Replacement for useState which depends on prop value and avoids unnecessary re-renders.
 */
export function useStateBasedOnProps(propValue) {
    const [, update] = useState(false);

    const [state, setState] = useMemo(() => {
        const state = {};
        return [
            state,
            (value) => {
                if (value instanceof Function) {
                    state.value = value(state.value);
                } else {
                    state.value = value;
                }
                update((tick) => !tick);
            }
        ];
    }, [update]);

    if (state.prevPropValue !== propValue) {
        state.prevPropValue = propValue;
        state.value = propValue;
    }

    return [state.value, setState];
}

Here is a usage example:

function IncButton({initialValue}) {
    const [value, setValue] = useStateBasedOnProps(initialValue);
    const increment = useCallback(() => setValue((prev) => prev + 1), [setValue]);
    return <button onClick={increment}>Click to increment: {value}</button>;
}

完整示例:https://gist.github.com/mdevils/b861610780300784292194f65886517a


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接