3

In a react project, there is a globally available context for alert and messages. In any given component, I can use a context.setAlert() to put up a message.

I have some API fetching logic extracted into a utility function. It's not a component. Components import and use this function in various ways prior to render, sort of like this:

//MyComponent.js
import {fetchFromApi} from "../util"
import AlertContext from "../contexts/alert"

const MyComponent = () => {
  const alertContext = useContext(AlertContext)
  useEffect(() => 
    fetchFromApi(...)
      .then(() => alertContext.setAlert('All set'), [])
  return <Something/>
}

So in fetchFromApi, I'd like to be able to use that AlertContext.setAlert method. Something like this:

// util.js
export const fetchFromApi = (params) => 
  fetch("...")
    .then(something)
    .then(somethingelse)
    .then(response => {
      // set alert from the AlertContext is what I want to access
      if (response.status === 500) alertContext.setAlert('oh no')
      return response
    })

I'm not sure if/how I can update a context state outside of a component. Is this possible?

Scribblemacher
  • 1,518
  • 1
  • 16
  • 31

2 Answers2

3

You can mutate an object and set a setAlert property in the context provider. This object can be used by fetchApi. I assume fetchApi is only called from components within the Alert context so it's guaranteed to be set when fetchApi uses setAlert in the object:

const passSetAlert = { setAlert: (x) => x };
const AlertContext = React.createContext();// mutate passSetAlert
function AlertProvider(props) {
  const [alert, setAlert] = React.useState('AA');
  passSetAlert.setAlert = setAlert;
  return (
    <AlertContext.Provider value={{ alert, setAlert }}>
      {props.children}
    </AlertContext.Provider>
  );
}
const fetchFromApi = (message) =>
  setTimeout(() => {
    passSetAlert.setAlert(message);
  }, 2000);

function App() {
  const [count, setCount] = React.useState(0);
  const alertContext = React.useContext(AlertContext);
  const click = () => {
    const newCount = count + 1;
    setCount(newCount);
    fetchFromApi(newCount);
  };
  return (
    <div>
      <button onClick={click}>change alert</button>
      Alert is: {alertContext.alert}
    </div>
  );
}
ReactDOM.render(
  <AlertProvider>
    <App />
  </AlertProvider>,
  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>
HMR
  • 37,593
  • 24
  • 91
  • 160
0

Modify the code for fetchFromApi so that it takes an additional parameter config with a callback function. You can invoke callbacks accordingly.

Hope this help.

Harish Dhami
  • 1,016
  • 1
  • 7
  • 10
  • Thanks. I'd like to avoid doing something like this, because I'd need to do it to every single call to `fetchFromApi` since the behavior would always be the same. – Scribblemacher May 16 '20 at 03:14