import React from "react";
import { render } from "react-dom";
import { createStore, applyMiddleware } from "redux";
import { Provider, connect } from "react-redux";
import thunk from "redux-thunk";
const disabled = (state = true, action) => {
return action.type === "TOGGLE" ? !state : state;
};
class Button extends React.Component {
componentDidUpdate(prevProps) {
if (prevProps.disabled !== this.props.disabled && !this.props.disabled) {
// this.ref.focus(); // uncomment this to see the desired effect
}
}
render() {
const { props } = this;
console.log("rendering", props.value);
return (
<div>
<input
type="checkbox"
onClick={() => {
props.toggle();
this.ref.focus(); // doesn't work
}}
/>
<input
disabled={props.disabled}
ref={ref => {
this.ref = ref;
}}
/>
</div>
);
}
}
const toggle = () => ({
type: "TOGGLE"
});
const A = connect(state => ({ disabled: state }), { toggle })(Button);
const App = () => (
<Provider store={createStore(disabled, applyMiddleware(thunk))}>
<A />
</Provider>
);
render(<App />, document.getElementById("root"));
我想在选中复选框时关注input
。
但是,只有在使用this.ref.focus()
重新呈现组件后才能调用props.disabled === false
,因为input
的{{1}}道具无法集中。
如果我在disabled
中执行逻辑,我就能实现我想要的。但这不是一个干净的解决方案,因为逻辑特定于componentDidUpdate
处理程序而不是生命周期事件。
还有其他方法可以实现吗? (最好使用工作代码框示例)
答案 0 :(得分:2)
我认为最好的办法是不依靠refs use state来管理焦点。
此解决方案使用输入上的autoFocus
道具,并在复选框的状态发生变化时对其进行修改。
import React from "react";
import { render } from "react-dom";
import { createStore, applyMiddleware } from "redux";
import { Provider, connect } from "react-redux";
import thunk from "redux-thunk";
const disabled = (state = true, action) => {
return action.type === "TOGGLE" ? !state : state;
};
class Button extends React.Component {
state = {
checked: false,
focus: false
};
componentDidUpdate(prevProps, prevState) {
if (prevState.checked !== this.state.checked) {
this.props.toggle();
this.setState({
focus: this.state.checked
});
}
}
render() {
const { props } = this;
const { checked, focus } = this.state;
console.log("rendering", props.value, checked);
return (
<div>
<input
type="checkbox"
checked={checked}
onClick={({ target }) => {
this.setState({ checked: target.checked });
}}
/>
<input key={`input_${checked}`} autoFocus={focus} />
</div>
);
}
}
const toggle = () => ({
type: "TOGGLE"
});
const A = connect(state => ({ disabled: state }), { toggle })(Button);
const App = () => (
<Provider store={createStore(disabled, applyMiddleware(thunk))}>
<A />
</Provider>
);
render(<App />, document.getElementById("root"));
我不确定原因,但在先前禁用组件时更改autoFocus
道具不会触发重新渲染的输入。所以我还在输入中添加了一个键来强制它。
答案 1 :(得分:2)
这是一个假设情况和REACT中的一个开放问题(同时不是)因为它与自动对焦的HTML规范(https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#Attributes#autofocus)一致。焦点是装饰性的非常棘手的事情之一,因为它是共享的全球状态的一部分。如果2个不相关的组件声明它们应该集中在一个渲染过程中,谁是对的?因此,REACT为您提供了自己管理该状态的钩子,但它不会为您做到这一点(因此,就像您正在使用的那样工作的地方)。
但是如果REACT添加了专注于渲染的选项(可能只是autoFocusOnRender),那就好了,如果有多个事情需要立即聚焦,那么让文档警告人们行为。理想情况下,这不会发生,因为具有良好用户体验的应用程序将具有在不同输入上调用autoFocusOnRender的特定条件。
我建议你做的是最好的方法:)。希望我们在REACT中对此进行改进。
答案 2 :(得分:1)
由于数据流的原因,我认为在您执行state
调用之前,您可以放心更新的Redux focus()
数据:
toggleThunk
,并等待其解决方案then
调度同步操作以更新state
(新state
数据),并等待其解析(?)then
focus()
您的参考https://codesandbox.io/s/r57v8r39om
请注意,在您的OP中,您的toggle()
动作创建者不是thunk。此外,强制执行您的thunk返回Promise是一个很好的规则,这样您就可以按照您描述的方式控制数据流。
import React from "react";
import { render } from "react-dom";
import { createStore, applyMiddleware } from "redux";
import { Provider, connect } from "react-redux";
import thunk from "redux-thunk";
const disabled = (state = true, action) => {
return action.type === "TOGGLE" ? !state : state;
};
class Button extends React.Component {
textInput = React.createRef();
handleClick = () => {
const { toggleThunk } = this.props;
toggleThunk().then(() => {
this.textInput.current.focus();
});
};
render() {
const { disabled, value } = this.props;
return (
<div>
<input type="checkbox" onClick={this.handleClick} />
<input disabled={disabled} ref={this.textInput} />
</div>
);
}
}
// Action
const toggle = () => ({
type: "TOGGLE"
});
// Thunk
const toggleThunk = () => dispatch => {
// Do your async call().then...
return Promise.resolve().then(() => dispatch(toggle()));
};
const A = connect(state => ({ disabled: state }), { toggleThunk })(Button);
const App = () => (
<Provider store={createStore(disabled, applyMiddleware(thunk))}>
<A />
</Provider>
);
render(<App />, document.getElementById("root"));
答案 3 :(得分:0)
您可以使用prop和ref来管理它。引用将避免重新输入输入(即autoFocus
工作):
import React, { Component } from "react";
import { render } from "react-dom";
import { createStore, applyMiddleware } from "redux";
import { Provider, connect } from "react-redux";
import thunk from "redux-thunk";
const disabled = (state = true, action) => {
return action.type === "TOGGLE" ? !state : state;
};
class Button extends Component {
componentDidUpdate(prevProps) {
if (!this.props.disabled && prevProps.disabled) {
this.ref.focus();
}
}
render() {
const { disabled } = this.props;
return (
<div>
<input
type="checkbox"
checked={!disabled}
onClick={() => {
this.props.toggle();
}}
/>
<input
disabled={disabled}
ref={ref => {
this.ref = ref;
}}
/>
</div>
);
}
}
const toggle = () => ({
type: "TOGGLE"
});
const A = connect(state => ({ disabled: state }), { toggle })(Button);
const App = () => (
<Provider store={createStore(disabled, applyMiddleware(thunk))}>
<A />
</Provider>
);
render(<App />, document.getElementById("root"));