admin管理员组文章数量:1302428
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 {
ponentDidUpdate(prevProps) {
if (prevProps.disabled !== this.props.disabled && !this.props.disabled) {
// this.ref.focus(); // unment 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"));
I want to focus the input
when the checkbox is checked.
However, this.ref.focus()
must be called only after the ponent re-renders with props.disabled === false
, as an input
with disabled
prop cannot be focused.
If I do the logic in ponentDidUpdate
, I'm able to achieve what I want. But this is not a clean solution as the logic is specific to the onClick
handler rather than a lifecycle event.
Is there any other way to acplish this? (preferably with a working codesandbox example)
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 {
ponentDidUpdate(prevProps) {
if (prevProps.disabled !== this.props.disabled && !this.props.disabled) {
// this.ref.focus(); // unment 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"));
I want to focus the input
when the checkbox is checked.
However, this.ref.focus()
must be called only after the ponent re-renders with props.disabled === false
, as an input
with disabled
prop cannot be focused.
If I do the logic in ponentDidUpdate
, I'm able to achieve what I want. But this is not a clean solution as the logic is specific to the onClick
handler rather than a lifecycle event.
Is there any other way to acplish this? (preferably with a working codesandbox example)
Share Improve this question edited May 26, 2018 at 0:49 Avery235 asked Apr 18, 2018 at 5:43 Avery235Avery235 5,32616 gold badges57 silver badges87 bronze badges 2-
1
IMO, update in the
ponentDidUpdate
is the right way, because re-render and focus is both ponent's state and behavior, you cannot decouple them cleanly. I would even say you should move the toggle state into the ponent and just have some callback props foronToggle
andonClick
. – Tr1et Commented May 28, 2018 at 3:10 - 1 @Avery235 what makes you say "this is not a clean solution as the logic is specific to the onClick handler rather than a lifecycle event"? – duhaime Commented Jun 1, 2018 at 19:47
4 Answers
Reset to default 2 +25I think the best thing to do is not rely on refs use state to manage the focus.
This solution instead uses the autoFocus
prop on the input, and modifies it when the state of the checkbox changes.
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
};
ponentDidUpdate(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"));
I'm not sure why, but changing the autoFocus
prop when the ponent was previously disabled doesn't trigger the input to be re-rendered. So I've also added a key to the input to force it.
This is an hypothetical situation and an open issue in REACT(at the same time NOT) since it is consistent with the HTML spec for autofocus (https://developer.mozilla/en-US/docs/Web/HTML/Element/input#Attributes#autofocus). Focus is one of those things that is really tricky to do decoratively because it's part of a shared global state. If 2 unrelated ponents declare that they should be focused in a single render pass, who is right? So REACT give you the hooks to manage that state yourself but it won't do it for you (thus where the work around like the one your are using came).
But It would be great if REACT added the option to focus on render (could be just autoFocusOnRender), and just have the docs warn people of the behavior if multiple things call for focus at once. Ideally this wouldn't happen because an app with good UX would have specific conditions for calling autoFocusOnRender on different inputs.
I would Suggest what you have done is the best way of doing it :). Hope we get an enhancement for this in REACT.
I think that you can have confidence that the updated Redux state
data is there before you perform your focus()
call, because of the data flow:
- Dispatch async action
toggleThunk
, and wait for its resolution then
dispatch synchronous action to update thestate
(newstate
data), and wait for its resolution (?)then
focus()
your ref
https://codesandbox.io/s/r57v8r39om
Note that in your OP, your toggle()
action creator is not a thunk. Also, it's a good rule to enforce that your thunks return a Promise so that you can control data flow in the way you're describing.
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"));
You can manage this with the prop and a ref. The ref will avoid the need to rerender the input (i.e. for autoFocus
to work):
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 {
ponentDidUpdate(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"));
本文标签: javascriptReduxthunk dispatch an action and wait for rerenderStack Overflow
版权声明:本文标题:javascript - Redux-thunk dispatch an action and wait for re-render - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741686855a2392490.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论