有很多类似的问题,但是我找不到与我的难题相符的问题。
我有一个反应组件(一个径向旋钮控件-有点像一个滑块)。 我想实现两个结果:
我已经拔掉头发-但是有一个可行的解决方案似乎违反了反应原则。
我有 knob.js 作为包裹第三方旋钮组件的react组件,而我有 app.js 作为父组件。
在结子.js中,我们有:
export default class MyKnob extends React.Component {
constructor(props, context) {
super(props, context)
this.state = {
size: props.size || 100,
radius: (props.value/2).toString(),
fontSize: (props.size * .2)
}
if (props.value){
console.log("setting value prop", props.value)
this.state.value = props.value
} else {
this.state.value = 25 // any old default value
}
}
要处理来自父代(app.js)的更新,我将其放在结子.js中:
// this is required to allow changes at the parent to update the knob
componentDidUpdate(prevProps) {
if (prevProps.value !== this.props.value) {
this.setState({value: this.props.value})
}
console.log("updating knob from parent", value)
}
然后将旋钮值的更改传递回父级,我有:
handleOnChange = (e)=>{
//this.setState({value: e}) <--used to be required until line below inserted.
this.props.handleChangePan(e)
}
这也有效,但会触发警告:
在渲染其他组件(
App
)时无法更新组件(Knob
)
render(){
return (
<Styles font-size={this.state.fontSize}>
<Knob size={this.state.size}
angleOffset={220}
angleRange={280}
steps={10}
min={0}
max={100}
value={this.state.value}
ref={this.ref}
onChange={value => this.handleOnChange(value)}
>
...
现在转到app.js:
function App() {
const [panLevel, setPanLevel] = useState(50);
// called by the child knob component. works -- but creates the warning
function handleChangePan(e){
setPanLevel(e)
}
// helper function for testing
function changePan(e){
if (panLevel + 10>100){
setPanLevel(0)
} else {
setPanLevel(panLevel+10)
}
}
return (
<div className="App">
....
<div className='mixer'>
<div key={1} className='vStrip'>
<Knob size={150} value={panLevel} handleChangePan = {(e) => handleChangePan(e)}/>
</div>
<button onClick={(e) => changePan(e)}>CLICK ME TO INCREMENT BY 10</button>
...
</div>
所以-它起作用了-但是我违反了反应原理-我还没有找到使外部“旋钮值”和内部“旋钮值”保持同步的另一种方法。
只要进一步弄乱我的头,如果我在“ handleOnChange”中消除了对父级的冒泡-大概会触发prop的更改->状态向下层叠-我不仅不与父级缺乏同步-但我还需要恢复下面的setState,以便通过旋转(鼠标等._)使旋钮起作用!这会产生另一个警告:
在现有状态转换过程中更新...
太难受了。要求提供建议并深表感谢。长篇文章的Apols。
handleOnChange = (e)=>{
//this.setState({value: e})
**this.props.handleChangePan(e)**
}
在另一篇文章中建议,应该将setState包装到useEffect中-但是我不知道该怎么做-更不用说这是否是正确的方法了。
答案 0 :(得分:5)
如果在渲染子级(旋钮)时设置了父级(应用程序)状态,则会显示错误消息。
在您的情况下,正在渲染App时,旋钮的onChange()
会在加载时触发,然后调用this.handleOnChange()
,然后this.props.handleChangePan()
拥有App的setPanLevel()
。
要使用useEffect()
进行修复:
knob.js
中,您可以像在App中一样首先存储panLevel
作为状态,而不是直接调用this.props.handleChangePan()
来调用App的setPanLevel()
。useEffect(_=>props.handleChangePan(panLevel),[panLevel])
通过setPanLevel()
调用App的useEffect()
。您的toggle.js如下所示:
function Knob(props){
let [panLevel, setPanLevel] = useState(50);
useEffect(_=>{
props.handleChangePan(panLevel);
}, [panLevel]);
return *** Knob that does not call props.handleChangePan(), but call setPanLevel() instead ***;
}
在渲染完成后,在setState()
内部调用的 useEffect()
将生效。
简而言之,在第一次渲染时,您无法在setState()
之外调用父级的useEffect()
,否则会出现错误消息。