我正在尝试从另一个函数更新状态的值。
我已经这样声明了他们:
const App () => {
const [positionAX, setPositionAX] = useState(100)
const [positionAY, setPositionAY] = useState(100)
}
App
之外,我还有另一个功能:
const setPosition = () => {
setPositionAX(200);
setPositionAY(200);
}
致电setPosition()
时收到错误消息:
无法找到变量:setPositionAX
找不到变量:setPositionAY
我试图将const [positionAX, setPositionAX] = useState(100)
移到文件的顶部,但出现错误:
无效的挂钩调用。只能在函数体内调用钩子。
如何从其他函数更新该变量?
我似乎无法正常工作,我不确定自己做错了什么
这是您给我的代码的现场小吃示例:https://snack.expo.io/Cd!hwDQ0m
如果我添加警报:
alert("pressed")
进入handleClick函数,由于某些原因,它不能与setPositionA([200, 200])
一起使用。
另请注意: 在我的应用示例中,有两个功能以及设置状态的功能和显示状态的功能是分开的,这就是为什么我要在最初的Stack溢出帖子中提出要解决的问题。
以下是我要达到的目标的一个小例子:
https://snack.expo.io/6TUAkpCZM
我使用'positionX'和'positionY'作为变量来定义要渲染的元素的顶部和左侧位置,但是positionX
和positionY
变量的实际设置在另一个功能中完成
我是一名初级开发人员,所以我真的要感谢您对此提供的所有帮助,如果这是超级nooby,也深表歉意。
我尝试了您的方法,但似乎无法正常工作。我想对我想要实现的目标以及在哪里实现实现感到困惑。因为我是noobie开发人员,所以这可能是我的错,请原谅我。我不想只发布我的所有代码,因为我认为人们对此不屑一顾,以至于人们只能转储代码并喜欢“修复此问题”-但是,在这种情况下,这可能是最好的哈哈。
这是我的代码的完整小吃:https://snack.expo.io/uk8yb5xys
这是我想要的:
指示用户在单词“开始”到单词“结束”之间划一条线。如果绘制了线并且用户线的起点和终点与给定的路径匹配,则会向他们显示另一条线进行绘制,并且“开始”和“结束”的位置将移动,以便用户知道在哪里绘制。
有一条评论行:
///在这里我需要更新开始和结束的值
这是我需要“开始”和“结束”两个词移到新位置的地方。
目前,当您绘制一条完整的线并从屏幕上移开手指时,什么也没有发生。但是,当您将手指放在起点和终点时,请移动。当用户抬起手指时,我需要它们移动。
感谢您的宝贵时间,谢谢。
答案 0 :(得分:4)
三个选项:
const setPositionFactory = (setPositionAX, setPositionAY) => (pos = 200) => {
setPositionAX(pos);
setPositionAY(pos);
}
因此您可以通过多种方式在组件内部调用它:
直:setPositionFactory(setPositionAX, setPositionAY)(); // default: 200
或保留该功能以备后用:
const setPosition = setPositionFactory(setPositionAX, setPositionAY);
// ...
setPosition(220);
App
的道具,并更新组件内部的状态。会更习惯。Observable
与rxjs一起使用,并订阅您的组件。在您的问题的代码中添加可观察对象是:
import { positionObs } from './observables';
const App () => {
const [positionAX, setPositionAX] = useState(100);
const [positionAY, setPositionAY] = useState(100);
positionObs.subscribe((pos) => {
setPositionAX(pos);
setPositionAY(pos);
});
}
然后在observables
import { Subject } from 'rxjs';
export const positionObs = new Subject();
export const setPosition = () => {
positionObs.next(200);
};
答案 1 :(得分:3)
从useState
获得的状态设置器特定于组件实例,该组件实例是在首次调用组件函数时创建的。
您有几种选择:
您可以将setPositionAX
和setPositionAY
作为参数传递到setPosition
中。
您可以将setPosition
函数放入您的组件函数中。 (是的,每次都会重新创建,但这完全没有害处。)
您可以创建自己的位置信息挂钩。
#3看起来像这样:
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 [xA, yA, setPositionA] = usePosition(0, 0);
// ...
setPositionA(200, 200);
或者如果您希望使用元组作为位置信息:
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 ];
};
然后在您的组件函数中:
const [positionA, setPositionA] = usePosition([0, 0]);
// ^−−−−^−−−−− passing in a tuple
// ...
setPositionA([200, 200]);
以下是使用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>
答案 2 :(得分:0)
由于放置在功能组件外部的功能逻辑与内部的逻辑紧密结合,因此建议将其移入内部。
您的文件如下所示:
import React, { useState, useRef } from 'react';
import { Text, View, Button } from 'react-native';
const Sandbox = () => {
const [positionA, setPositionA] = usePosition([0, 0]);
const [x, setX] = useState(0);
const [y, setY] = useState(0);
const usePosition = ([_x = 0, _y = 0] = []) => {
const { current: setPosition } = useRef(function setPosition([_x, _y] = []) {
setX(x);
setY(y);
});
return [[x, y], setPosition];
};
const handleClick = () => {
setPositionA([200, 200]);
};
return (
<View>
<Text style={{ marginTop: 40, marginLeft: 40 }}>Position: {positionA.join()}</Text>
<Button onPress={handleClick} title="Update Position"></Button>
</View>
);
};
export default Sandbox
答案 3 :(得分:0)
useState
允许React对该特定组件的重新渲染进行排队
您可以将APP.js函数向下传递到AnotherComponent,然后从AnotherComponent调用App.js函数,并在两个组件上管理状态,并在结构复杂时使用其他方式使用 REDUX
您可以在此处测试小吃演示:https://snack.expo.io/iVeYXymID
根据已修改的问题解决: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",
},
});
答案 4 :(得分:0)
只能在组件中使用挂钩!
答案很简单:将要使用钩子的方法放在组件内部。
const App () => {
const setPosition = () => {
setPositionAX(200);
setPositionAY(200);
}
const [positionAX, setPositionAX] = useState(100)
const [positionAY, setPositionAY] = useState(100)
}
您认为您真的必须在单独的函数中调用useState吗?
答案 5 :(得分:0)
首先让我开始解释为何您在博览会零食上的应用无法正常工作(鉴于此https://snack.expo.io/uk8yb5xys是您原始应用的零食)。您的数据(currentStroke和currentPath)仅存储在react之外的变量中-它不是被动的。这意味着虽然是,但您的组件在渲染时会读取此数据(例如,Start和End对象根据该数据确定其位置),在初始渲染之后,更改currentStroke不会更改Start和End的位置,因为它不会触发重新渲染这些组件。因此,数据可以“正确”更改,但是组件不会使用新数据重绘自身。如果您希望数据更改反映在应用程序中,则绝对应该保持状态
在这里,无论如何我都只会使您的CharacterTrace
每隔半秒更新一次,这不是您应该做的事情,但这是为了说明这一点:现在它将每隔半秒重绘一次,然后对非反应性currentStroke
进行“提取”更改:
const CharacterTrace = () => {
const [path, setPath] = useState(emptyPath);
//
const [_,updateState] = useState()
React.useEffect(() => {
setInterval(() => {
console.log('updating state only to trigger re-render...')
updateState(Math.random())
}, 500)
} , [])
//
...
此更改的小吃:https://snack.expo.io/wwJAtOjfv
因此,要解决您的问题,您应该对应用进行重新处理以将currentStroke
存储在状态中,例如存储在CharacterTrace
组件const [currentStroke, setCurrentStroke] = useState(1)
中。然后,您将setCurrentStroke
传递给基础组件以执行其逻辑并在必要时调用它
那应该以正确的方式解决您的问题,但是为了回答关于如何在组件外部调用setter的原始问题:
。
const A = () => {
const [x, setX] = useState(0)
return (
<B setX={setX} />
)
}
const B = ({ setX }) => {
useEffect(() => {
setX(123)
}, [])
// ...
}
如果要从父组件更改它,实际上应该做的是lift the state up到该父组件,因为它属于该父组件
如果您需要从react组件外部更改状态(几乎不需要做),那么为了回答完整,这就是您可以做到的方式
。
let visibleSetXReference = null
const App = () => {
const [x, setX] = useState(0)
visibleSetXReference = setX
return (
<View>
<Text>{x}</Text>
</View>
)
}
const setXFromOutsideApp = (newX) => {
if (!visibleSetXReference) {
console.log("App hasn't rendered yet")
} else {
visibleSetXReference(newX)
}
}
setXFromOutsideApp(123)
setXFromOutsideApp(456)
setXFromOutsideApp(789)
答案 6 :(得分:0)
我们可以将值(在我们的示例中为200)作为道具发送到App函数组件。 React.useEffect()
有助于聆听道具变化。因此,只要我们想设置值,就可以。
Parent.js
class Parent extends React.Component {
constructor(props) {
super(props)
this.state = {
x: 0,
y: 0
}
}
clickHandler = () => {
this.setState({
x : 200,
y : 200
})
}
render() {
return (
<div>
<Button onPress={this.clickHandler} title="set 200"/>
<App setX={this.state.x} setY={this.state.y} />
</div>
)
}
}
App.js
const App = (props) => {
const [positionAX, setPositionAX] = React.useState(100)
const [positionAY, setPositionAY] = React.useState(100)
React.useEffect(()=>{
setPositionAX(props.setX)
setPositionAY(props.setY)
})
return <div>{positionAX}, {positionAY}</div> //displays the position
}