Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Native - how to use 'useState()' in another function

I am trying to update the value of a state from another function.

I have declared them like so:


const App () => {

const [positionAX, setPositionAX] = useState(100)
const [positionAY, setPositionAY] = useState(100)

}

outside of App I have another function:

const setPosition = () => {
    setPositionAX(200);
    setPositionAY(200);

}

When I call setPosition() I get the error:

Cant find Variable: setPositionAX

Cant find Variable: setPositionAY

I tried to move const [positionAX, setPositionAX] = useState(100) to the top of my file but then I got the error:

Invalid hook call. Hooks can only be called inside of the body of a function.

How do I update that variable from other functions?

Edit 1 for @t-j-crowder

I can't seem to get this to work, I am not sure what I am doing wrong

Here is a live snack example of the code you gave me: https://snack.expo.io/Cd!hwDQ0m

If I add an alert:

alert("pressed")

Into the handleClick function, that works so for some reason it is just not working with the setPositionA([200, 200]).

Also as an additional note: In my app example, there are two functions and the functionality of setting the state, and the functionality of displaying the state are separated which is why I am getting the issue I meant to present in my initial Stack overflow post.

Here is a snack of that example of what I am trying to achieve:

https://snack.expo.io/6TUAkpCZM

I am using the 'positionX' and 'positionY' as vars to define the Top and Left position of an element which are rendered out, however, the actual setting of the positionX and positionY variable is done in another function

I am a junior developer so I really want to thank you for all your help with this and also apologise if this is super nooby.

Edit 2 for: Muhammad Numan

I tried your way and I just can't seem to get it to work. I think there is some confusion as to what I want to achieve and where I want to achieve it. That is probably my fault as I am a noobie developer so please forgive me. I didn't want to just post all my code as I think its frowned upon for people to just code dump and be like "fix this" - however, for this situation its probably best haha.

Here is a full snack of my code: https://snack.expo.io/uk8yb5xys

What I want is this:

The user is instructed to draw a line from the word "start" to the word "end". If the line is drawn and the start point and endpoint of the users line match the given path, they are then presented with another line to draw and the position of "start" and "end" will move so the user knows where to draw.

There is a comment line:

// HERE IS WHERE I NEED TO UPDATE THE VALUES FOR START AND END

This is where I need the word "start" and the word "end" to move to their new positions.

At the moment, when you draw a complete line and remove your finger from the screen - nothing happens. However, when you put your finger down the start and end do move. I need them to move when the user lifts up their finger.

I appreciate your time with this so thank you.

like image 484
JamesG Avatar asked May 14 '20 09:05

JamesG


People also ask

How is useState used in another functional component?

But useState doesn't return just a variable as the previous examples imply. This way, you can use the state variable in the functional component like any other variable: const Message = () => { const [message, setMessage] = useState( '' ); return ( <p> <strong>{message}</strong> </p> ); };

Can I use useState inside a function?

The useState hook is used to update the state in a React component. It is a hook that takes the initial state as an argument and returns an array of two entries. It can be used in a React class based component as well as a functional component (declared using the function or const keyword).

How do you pass a state from one functional component to another?

Sending state/props to another component using the onClick event: So first we store the state/props into the parent component i.e in which component where we trigger the onClick event. Then to pass the state into another component, we simply pass it as a prop.


3 Answers

Three options:

A. Send those functions as params and use curried functions:

const setPositionFactory = (setPositionAX, setPositionAY) => (pos = 200) => {
    setPositionAX(pos);
    setPositionAY(pos);
}

So you can call it inside your component in many ways:

Directly:
setPositionFactory(setPositionAX, setPositionAY)(); // default: 200
or Keeping the function for later use:
const setPosition = setPositionFactory(setPositionAX, setPositionAY);

// ...

setPosition(220);

B. Get those values as props of App and update state inside the component. It would be more idiomatic.

C. Use an Observable with rxjs and subscribe your component to it.

Adding observables to your question's code would be:

import { positionObs } from './observables';

const App () => {
    const [positionAX, setPositionAX] = useState(100);
    const [positionAY, setPositionAY] = useState(100);

    positionObs.subscribe((pos) => {
        setPositionAX(pos);
        setPositionAY(pos);
    });
}

Then in observables

import { Subject } from 'rxjs';

export const positionObs = new Subject();

export const setPosition = () => {
    positionObs.next(200);
};

like image 192
Ignacio Lago Avatar answered Oct 19 '22 02:10

Ignacio Lago


The state setters you get from useState are specific to the component instance, created when the component function is called the first time.

You have a few options:

  1. You can pass setPositionAX and setPositionAY into setPosition as arguments.

  2. You can put your setPosition function in your component function. (Yes, it'll be recreated each time, but that's fairly harmless.)

  3. You can create your own hook for position information.

#3 looks like this:

const usePosition = (_x = 0, _y = 0) => {
    const [x, setX] = useState(_x);
    const [y, setY] = useState(_y);
    const {current: setPosition} = useRef(function setPosition(_x = 0, _y = 0) {
        setX(x);
        setY(y);
    });

    return [x, y, setPosition];
};

Then in your component function:

const [xA, yA, setPositionA] = usePosition(0, 0);

// ...
setPositionA(200, 200);

Or if you prefer a tuple for the position information:

const usePosition = ([_x = 0, _y = 0] = []) => {
//                    ^^^^^^^^^^^^^^^ ^^^^^−− optional default value
//                     \             \
//                      +−−−−−−−−−−−−+−−−−−−− destructuring
    const [x, setX] = useState(_x);
    const [y, setY] = useState(_y);
    // (Ensure the setter is consistent across calls, like React does)
    const {current: setPosition} = useRef(function setPosition([_x = 0, _y = 0] = []) {
        setX(x);
        setY(y);
    });

    return [ [x, y], setPosition ];
};

Then in your component function:

const [positionA, setPositionA] = usePosition([0, 0]);
//                                            ^−−−−^−−−−− passing in a tuple

// ...
setPositionA([200, 200]);

Here's an example of that tuple version using React:

const { useState, useRef } = React;

const usePosition = ([_x = 0, _y = 0] = []) => {
    const [x, setX] = useState(_x);
    const [y, setY] = useState(_y);
    const {current: setPosition} = useRef(function setPosition([_x = 0, _y = 0] = []) {
        setX(x);
        setY(y);
    });

    return [ [x, y], setPosition ];
};

const Example = () => {
    const [positionA, setPositionA] = usePosition([0, 0]);
    const handleClick = () => {
        setPositionA([200, 200]);
    };
    
    return (
        <div>
            <div>Position: {positionA.join()}</div>
            <input type="button" onClick={handleClick} value="Set 200x200" />
        </div>
    );
};

ReactDOM.render(<Example/>, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>
like image 32
T.J. Crowder Avatar answered Oct 19 '22 00:10

T.J. Crowder


useState allowing React to queue up the re-rendering of that specific component

you can pass APP.js function down to AnotherComponent and call App.js function from AnotherComponent and manage state on both component and other way to use REDUX if your structure is complex

you can test Snack Demo here: https://snack.expo.io/iVeYXymID

Fix according to edited question: https://snack.expo.io/i_RJHi0_v

import React, { useState, useRef } from "react";
import { Text, View, Button, StyleSheet } from "react-native";

const AnotherComponent = (props) => {
  const [positionXY, setPositionXY] = useState(props.StateValue);

  const handleClick = () => {
    const value = [positionXY[0]+5, positionXY[1]+5];
    setPositionXY(value);
    props.setPositionValues(value);
  };

  return (
    <View style={styles.anotherComponentStyle}>
      <Text>
        AnotherComponent PositionX: {positionXY[0]} PositionY: {positionXY[1]}
      </Text>
      <Button onPress={handleClick} title="Update Position"></Button>
    </View>
  );
};

const Sandbox = () => {
  const StateValue = [0, 0];
  const [positionXY, setPositionXY] = useState(StateValue);

  const setPositionValues = (value) => {
    setPositionXY(value);
  };

  return (
    <View style={styles.container}>
      <AnotherComponent setPositionValues={setPositionValues} StateValue={StateValue} />
      <Text
        style={{ positon: "absolute", top: positionXY[0], left: positionXY[1] }}
      >
        Sandbox component PositionX: {positionXY[0]} PositionY: {positionXY[1]}
      </Text>
    </View>
  );
};

export default Sandbox;

const styles = StyleSheet.create({
  container: { flex: 1, justifyContent: "center", alignContent: "center" },
  anotherComponentStyle: {
    backgroundColor: "#eeeffe",
    borderBottomColor: "#000000",
    borderBottomWidth: 2,
    marginBottom: 200,
    justifyContent: "center",
    alignItems: "center",
  },
});

enter image description here

like image 40
Muhammad Numan Avatar answered Oct 19 '22 01:10

Muhammad Numan