How to access state in function which is in another file

Viewed 7110

today I’ve finished my first React App - Calculator.

I though it will be better to have App.js clean and place all of the logic functions in separated file Logic.js and also all of the design functions (changing output font size depending on length of number or button on click animations) in for example Effects.js.

Problem occured when i took functions ,placed them in new file (of course exported them and also imported them in app.js).

Error says that setNumber(method which i call when i want to work with state),

number.firstNumber (firstNumber state which holds first clicked number),

And every state which im calling in this file (functions or conditions) is not defined ,so it means this file has no access to App.js state. Is there any way I can keep these functions separated and don’t have to rebuild whole app so I can achieve clean code ?

App.js creating states with which I work in these functions

const [number, setNumber] = useState({
firstNumber: "",
secondNumber: "",
operator: "",
result: "0",
displayed: "0",
cButton: "AC",
cButtonCheck: false,
numToReset: false,
sizeOfOutput: "1em",
isOrange: false
});

Example of function in new file which gets error of setNumber is not defined

function turnOnOrange(operator) {
if (number.isOrange === false)     {
  setNumber(prevState => ({
    ...prevState,
    isOrange: true,
    whichOrange: operator
  }));
  operator.className = "orangeActivated";
}
}

Thanks, link so you can see whole code

https://codesandbox.io/embed/youthful-platform-n6lvs?fontsize=14&hidenavigation=1&theme=dark&codemirror=1

4 Answers

I think you can define your own hooks to manage partial state since Hook was born to share state. Here is one of example:

yourHook.js


export function colorHook(initValue) {
  const [color, setColor] = React.useState(initValue);

  // Define whatever you want
  const setOrange = () => setColor({ isYellow: true }) 

  return [color, setColor, setOrange, // pass more ];
}

In your main file:


import { colorHook } from "./yourHook";

function yourComponent() {
  const [color, setColor, setOrange] = colorHook({ /* your value */ }) 
}

You can split the single state object you have in the App.js into multiple manageable cohesive states and then make your own custom hooks which would manipulate a part of the state you have just split.

For example for the turnOnOrange function, you can create new hook called useOrangeHook in its own file and import and use it as a hook:

import React, { useState } from "react";

export default function useOrangeHook(operator) {
  const [orangeState, setOrangeState] = useState({
    isOrange: false,
    whichOrange: ""
  });
  if (!orangeState.isOrange) {
    setOrangeState(prevState => ({
      ...prevState,
      isOrange: true,
      whichOrange: operator
    }));
    operator.className = "orangeActivated";
  }
  return orangeState;
}

Custom Hooks are a mechanism to reuse stateful logic. Check out the React guide here.

You should return these variables from the function and export the function not vice versa.

What you're trying to do is called custom hooks I'll give you an example in which you can reflect on:

useMyFirstCustomHook.js file
import React, { useState } from 'react';

const useMyFirstCustomHook = () => {
  const [count, setCount] = useState(0);
  // do some stuff
  return { count, setCount };
};

export default useMyFirstCustomHook;

App.js file
//..
import useMyFirstCustomHook from 'path/to/useMyFirstCustomHook';
//..
const { count, setCount } = useMyFirstCustomHook();
console.log(count);

First off, separating things makes a lot of sense, except when you're trying to manage state of a component. The reasoning here is that the state is wholly managed by the component including the lense variables, in your case [number, setNumber]. Having a function that calls these externally from the component raises a big red flag and as you've noticed does not work.

The reason it's not working is a matter of scope. The function turnOnOrange will not be called within the scope of the component itself. As you're using a function component, this has very little meaning so even a turnOnOrange.call(this, operator) isn't going to be much help.

So, whilst I applaud you for wanting to separate out your logic, in this case it is working against you and actually isn't very intuitive if you think about it. Your function turnOnOrange absolutely belongs inside that component.

Related