KKK
KKK

Reputation: 61

Context API updating state causes looped requests

I have some context. I store there user roles.

const RolesContext = createContext({roles: []});
function RolesContextProvider({children}) {
  const [roles, setRoles] = useState([]);

  async function check(newRoles) {
    const missing = compareArrayWithArray(newRoles, roles);
    if (missing.length !== 0) {
      await User.roles(newRoles).then(((res) => {
        const updatedRoles = roles.concat(res.data);
        setRoles(updatedRoles);
      }));
    }
  }

  const defaultContext = {
    roles,
    check,
  };

  return (
    <RolesContext.Provider value={defaultContext}>
      {children}
    </RolesContext.Provider>
  );
}
export {RolesContext, RolesContextProvider};

When initiating component I run check roles

export default function Users() {
  const UsersComposition = compose(
    connect(mapStateToProps, mapDispatchToProps)
  )(ConnectedUsers);
  const context = useContext(RolesContext);
  const {roles, check} = context;
  useEffect(() => {
    check(['roles', 'to', 'check']);
  }, [check]);
  return <UsersComposition roles={roles}/>;
};

What it does...App is crashing due to inifite update state loop. It makes dozens of same requests with same payload. How it should be done? Thanks for suggestions.

Upvotes: 0

Views: 644

Answers (2)

Yuri Fedotov
Yuri Fedotov

Reputation: 161

In order to fix the infinite loop you'll need to preserve check function's identity between renders. One way to do this is to save it with useRef (you'll need to pass existing roles as the second parameter):

const check = useRef(async (newRoles, roles) => {
    ...
});

const defaultContext = {
    roles,
    check: check.current,
};

You may also consider implementing Users as a class component and call check in componentDidMount:

class Users extends React.Component {
    static contextType = RolesContext;

    componentDidMount() {
        this.context.check(['roles', 'to', 'check']);
    }

    render() {
        ...
    }
}

Upvotes: 1

cbdeveloper
cbdeveloper

Reputation: 31425

It seems like you're calling check in a loop. See if I'm right.

  • RolesContextProvider provides the context roles and check
  • Users access the context and calls check
  • Check updates roles in RolesContextProvider with setRoles
  • setRoles triggers update in RolesContextProvider which changes the context
  • And the context change updates Users
  • And the cycle repeats

Upvotes: 0

Related Questions