admin管理员组

文章数量:1023204

The problem

I want to prevent the triggering of a useEffect hook which contains a useState value in the dependency array. To make it clear, look at the example.

Detailed explanation

In the initialization process a form will set some values therefore I defined a flag to prevent the other effect to execute before it is fully initialized, but I also don't want to trigger the second effect when the initialization flag state is changed.

Can I somehow prevent the tracking of this isInitialization state? I couldn't find a thread which is solves my problem. Would you maybe so kind to link the threads which are related to that? Thanks in advance.

Example

export function SomeComponent(props) {
  const [form] = useForm();
  const [settings, setSettings] = useState(props.initialSettings);
  const [isInitialization, setIsInitialization] = useState(true);

  /**
   * Updates the form settings state, just for initialization
   */
  useEffect(() => {
    if (isInitialization) {
      form.setFieldsValue({
        ...settings,
      });
      setIsInitialization(false); // <-- This should not trigger any useEffect
    }
  }, [settings, form, isInitialization]);

  useEffect(() => {
    if (props.settingsChanges && !isInitialization) {
      props.settingsChanges(settings, isValid && hasRequiredFields);
    }
  }, [settings, props, isInitialization]);
}

The problem

I want to prevent the triggering of a useEffect hook which contains a useState value in the dependency array. To make it clear, look at the example.

Detailed explanation

In the initialization process a form will set some values therefore I defined a flag to prevent the other effect to execute before it is fully initialized, but I also don't want to trigger the second effect when the initialization flag state is changed.

Can I somehow prevent the tracking of this isInitialization state? I couldn't find a thread which is solves my problem. Would you maybe so kind to link the threads which are related to that? Thanks in advance.

Example

export function SomeComponent(props) {
  const [form] = useForm();
  const [settings, setSettings] = useState(props.initialSettings);
  const [isInitialization, setIsInitialization] = useState(true);

  /**
   * Updates the form settings state, just for initialization
   */
  useEffect(() => {
    if (isInitialization) {
      form.setFieldsValue({
        ...settings,
      });
      setIsInitialization(false); // <-- This should not trigger any useEffect
    }
  }, [settings, form, isInitialization]);

  useEffect(() => {
    if (props.settingsChanges && !isInitialization) {
      props.settingsChanges(settings, isValid && hasRequiredFields);
    }
  }, [settings, props, isInitialization]);
}

Share Improve this question asked Mar 1, 2021 at 8:08 Ling VuLing Vu 5,1815 gold badges27 silver badges49 bronze badges 12
  • Remove all the effect dependencies so the effect callback runs only once when the ponent mounts. The official React docs are an amazing resource: Conditionally firing an effect. – Drew Reese Commented Mar 1, 2021 at 8:11
  • But settings, form are required for the initialization process. How do I do it exactly? – Ling Vu Commented Mar 1, 2021 at 8:12
  • 1 you can use variables in the effect without adding them to the dependency array. Adding them makes the effect run when the values changed. – ccarstens Commented Mar 1, 2021 at 8:13
  • Dependencies are used only to re-trigger an effect, they aren't used as arguments to a function or anything like that, the effect is guaranteed to run at least once. – Drew Reese Commented Mar 1, 2021 at 8:14
  • 1 Any chance you can just enhance your useForm hook to accept initialFieldValues? Because this approach of yours looks to me like the wrong end to fix it. – Martin Commented Mar 1, 2021 at 8:22
 |  Show 7 more ments

4 Answers 4

Reset to default 2

Try to delete isInitialization from dependency array.

  useEffect(() => {
    if (isInitialization) {
      form.setFieldsValue({
        ...settings,
      });
      setIsInitialization(false); // <-- This should not trigger any useEffect
    }
  }, [settings, form]);

As React docs says:

If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array ([]) as a second argument. This tells React that your effect doesn’t depend on any values from props or state, so it never needs to re-run. This isn’t handled as a special case — it follows directly from how the dependencies array always works.

UPDATE:

Building Your Own Hooks

Building your own Hooks lets you extract ponent logic into reusable functions.

If it is mon logic, then you can create custom hooks and share across ponents.

«useMyCustomHook.jsx» file:

import { useEffect } from 'react';

export default function useMyCustomHook(str) {
  useEffect(() => {
      console.log(`title`, title);
  });
}

and then use it in ponent:

function fooFunction(props) {
  const [firstName, setFirstName] = useState('');

  useMyCustomHook(`This is ${firstName}`);

  return (        
      <div>The firstname is {firstName}</div>
  );
}

Special thanks to @Martin. This is solution does not actually solve the problem of skip the useEffect triggering, but helped me to refactor my code even more. Maybe this is not related, but I still wanted to share my oute with you:

In the end it looks similar like this. The initialization flag is not needed anymore, because the custom hook does it already:

const useAntForm = (initalData) => {
  const [form] = useForm();
  form.setFieldsValue({
    ...initalData,
  });

  return form;
};

export function SomeComponent({
  settingsChanges,
  initialSettings,
}: SettingsProps) {
  const form = useAntForm(initialSettings);
  const [settings, setSettings] = useState(initialSettings);
  const [isValid, setIsValid] = useState(false);
  .
  .
  .
}

According to https://reactjs/docs/hooks-rules.html it is possible to leave out dependencies in the useEffect hook when they shouldn't trigger rerendering. Usually when leaving out dependencies it says

React Hook useEffect has a missing dependency: 'someDependency'. Either include it or remove the dependency array.

In order to fix this you just need to add https://www.npmjs./package/eslint-plugin-react-hooks to your eslint config which is mostly located in the package.json file

My config now looks like:

package.json

{
 ...
 "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ],
    "plugins": [
      "react-hooks"  // <-- Added this
    ]
  },
  ...
}

useRef()

You can use useRef to update a state without triggering useEffect dependencies. useRef updates don't cause re-renders or trigger effects.

Example:

const stateRef = useRef(0);

// Update stateRef without triggering useEffect
stateRef.current += 1;

Updating stateRef won't trigger any useEffect. Source

The problem

I want to prevent the triggering of a useEffect hook which contains a useState value in the dependency array. To make it clear, look at the example.

Detailed explanation

In the initialization process a form will set some values therefore I defined a flag to prevent the other effect to execute before it is fully initialized, but I also don't want to trigger the second effect when the initialization flag state is changed.

Can I somehow prevent the tracking of this isInitialization state? I couldn't find a thread which is solves my problem. Would you maybe so kind to link the threads which are related to that? Thanks in advance.

Example

export function SomeComponent(props) {
  const [form] = useForm();
  const [settings, setSettings] = useState(props.initialSettings);
  const [isInitialization, setIsInitialization] = useState(true);

  /**
   * Updates the form settings state, just for initialization
   */
  useEffect(() => {
    if (isInitialization) {
      form.setFieldsValue({
        ...settings,
      });
      setIsInitialization(false); // <-- This should not trigger any useEffect
    }
  }, [settings, form, isInitialization]);

  useEffect(() => {
    if (props.settingsChanges && !isInitialization) {
      props.settingsChanges(settings, isValid && hasRequiredFields);
    }
  }, [settings, props, isInitialization]);
}

The problem

I want to prevent the triggering of a useEffect hook which contains a useState value in the dependency array. To make it clear, look at the example.

Detailed explanation

In the initialization process a form will set some values therefore I defined a flag to prevent the other effect to execute before it is fully initialized, but I also don't want to trigger the second effect when the initialization flag state is changed.

Can I somehow prevent the tracking of this isInitialization state? I couldn't find a thread which is solves my problem. Would you maybe so kind to link the threads which are related to that? Thanks in advance.

Example

export function SomeComponent(props) {
  const [form] = useForm();
  const [settings, setSettings] = useState(props.initialSettings);
  const [isInitialization, setIsInitialization] = useState(true);

  /**
   * Updates the form settings state, just for initialization
   */
  useEffect(() => {
    if (isInitialization) {
      form.setFieldsValue({
        ...settings,
      });
      setIsInitialization(false); // <-- This should not trigger any useEffect
    }
  }, [settings, form, isInitialization]);

  useEffect(() => {
    if (props.settingsChanges && !isInitialization) {
      props.settingsChanges(settings, isValid && hasRequiredFields);
    }
  }, [settings, props, isInitialization]);
}

Share Improve this question asked Mar 1, 2021 at 8:08 Ling VuLing Vu 5,1815 gold badges27 silver badges49 bronze badges 12
  • Remove all the effect dependencies so the effect callback runs only once when the ponent mounts. The official React docs are an amazing resource: Conditionally firing an effect. – Drew Reese Commented Mar 1, 2021 at 8:11
  • But settings, form are required for the initialization process. How do I do it exactly? – Ling Vu Commented Mar 1, 2021 at 8:12
  • 1 you can use variables in the effect without adding them to the dependency array. Adding them makes the effect run when the values changed. – ccarstens Commented Mar 1, 2021 at 8:13
  • Dependencies are used only to re-trigger an effect, they aren't used as arguments to a function or anything like that, the effect is guaranteed to run at least once. – Drew Reese Commented Mar 1, 2021 at 8:14
  • 1 Any chance you can just enhance your useForm hook to accept initialFieldValues? Because this approach of yours looks to me like the wrong end to fix it. – Martin Commented Mar 1, 2021 at 8:22
 |  Show 7 more ments

4 Answers 4

Reset to default 2

Try to delete isInitialization from dependency array.

  useEffect(() => {
    if (isInitialization) {
      form.setFieldsValue({
        ...settings,
      });
      setIsInitialization(false); // <-- This should not trigger any useEffect
    }
  }, [settings, form]);

As React docs says:

If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array ([]) as a second argument. This tells React that your effect doesn’t depend on any values from props or state, so it never needs to re-run. This isn’t handled as a special case — it follows directly from how the dependencies array always works.

UPDATE:

Building Your Own Hooks

Building your own Hooks lets you extract ponent logic into reusable functions.

If it is mon logic, then you can create custom hooks and share across ponents.

«useMyCustomHook.jsx» file:

import { useEffect } from 'react';

export default function useMyCustomHook(str) {
  useEffect(() => {
      console.log(`title`, title);
  });
}

and then use it in ponent:

function fooFunction(props) {
  const [firstName, setFirstName] = useState('');

  useMyCustomHook(`This is ${firstName}`);

  return (        
      <div>The firstname is {firstName}</div>
  );
}

Special thanks to @Martin. This is solution does not actually solve the problem of skip the useEffect triggering, but helped me to refactor my code even more. Maybe this is not related, but I still wanted to share my oute with you:

In the end it looks similar like this. The initialization flag is not needed anymore, because the custom hook does it already:

const useAntForm = (initalData) => {
  const [form] = useForm();
  form.setFieldsValue({
    ...initalData,
  });

  return form;
};

export function SomeComponent({
  settingsChanges,
  initialSettings,
}: SettingsProps) {
  const form = useAntForm(initialSettings);
  const [settings, setSettings] = useState(initialSettings);
  const [isValid, setIsValid] = useState(false);
  .
  .
  .
}

According to https://reactjs/docs/hooks-rules.html it is possible to leave out dependencies in the useEffect hook when they shouldn't trigger rerendering. Usually when leaving out dependencies it says

React Hook useEffect has a missing dependency: 'someDependency'. Either include it or remove the dependency array.

In order to fix this you just need to add https://www.npmjs./package/eslint-plugin-react-hooks to your eslint config which is mostly located in the package.json file

My config now looks like:

package.json

{
 ...
 "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ],
    "plugins": [
      "react-hooks"  // <-- Added this
    ]
  },
  ...
}

useRef()

You can use useRef to update a state without triggering useEffect dependencies. useRef updates don't cause re-renders or trigger effects.

Example:

const stateRef = useRef(0);

// Update stateRef without triggering useEffect
stateRef.current += 1;

Updating stateRef won't trigger any useEffect. Source

本文标签: javascriptSet a state from useState without triggering useEffect hookStack Overflow