一个还原连接元件(图论)怎么知道什么时候重新渲染?

我可能遗漏了一些显而易见的事情我想澄清一下。

我的理解是。
在一个天真的反应组件,我们有 statesprops。用 setState更新 state会重新呈现整个组件。props大部分是只读的,更新它们是没有意义的。

在订阅 redux 存储的反应组件中,通过类似于 store.subscribe(render)的东西,它显然会在每次存储更新时重新呈现。

Response-redux 有一个帮助器 connect(),它将状态树的一部分(组件感兴趣的部分)和 actionCreators 作为 props注入到组件中,通常是通过类似

const TodoListComponent = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)

但是由于了解到 setState对于 TodoListComponent对还原状态树变化(重新呈现)作出反应是必不可少的,我在 TodoList组件文件中找不到任何与 statesetState相关的代码。上面写着:

const TodoList = ({ todos, onTodoClick }) => (
<ul>
{todos.map(todo =>
<Todo
key={todo.id}
{...todo}
onClick={() => onTodoClick(todo.id)}
/>
)}
</ul>
)

有人能告诉我我错过了什么吗?

另外,我在跟随 还原包附带的待办事项列表示例。

65491 次浏览

The connect function generates a wrapper component that subscribes to the store. When an action is dispatched, the wrapper component's callback is notified. It then runs your mapState function, and shallow-compares the result object from this time vs the result object from last time (so if you were to rewrite a redux store field with its same value, it would not trigger a re-render). If the results are different, then it passes the results to your "real" component" as props.

Dan Abramov wrote a great simplified version of connect at (connect.js) that illustrates the basic idea, although it doesn't show any of the optimization work. I also have links to a number of articles on Redux performance that discuss some related ideas.

update

React-Redux v6.0.0 made some major internal changes to how connected components receive their data from the store.

As part of that, I wrote a post that explains how the connect API and its internals work, and how they've changed over time:

Idiomatic Redux: The History and Implementation of React-Redux

My answer is a little out of left field. It sheds light on a problem that led me to this post. In my case it seemed the app was Not re-rendering, even though it received new props.
React devs had an answer to this often asked question something to the tune that if the (store) was mutated, 99% of the time that's the reason react won't re-render. Yet nothing about the other 1%. Mutation was not the case here.


TLDR;

componentWillReceiveProps is how the state can be kept synced with the new props.

Edge Case: Once state updates, then the app does re-render !


It turn out that if your app is using only state to display its elements, props can update, but state won't, so no re-render.

I had state that was dependent on props received from redux store. The data I needed wasn't in the store yet, so I fetched it from componentDidMount, as is proper. I got the props back, when my reducer updated store, because my component is connected via mapStateToProps. But the page didn't render, and state was still full of empty strings.

An example of this is say a user loaded an "edit post" page from a saved url. You have access to the postId from the url, but the info isn't in store yet, so you fetch it. The items on your page are controlled components - so all the data you're displaying is in state.

Using redux, the data was fetched, store was updated, and the component is connected, but the app didn't reflect the changes. On closer look, props were received, but app didn't update. state didn't update.

Well, props will update and propagate, but state won't. You need to specifically tell state to update.

You can't do this in render(), and componentDidMount already finished it's cycles.

componentWillReceiveProps is where you update state properties that depend on a changed prop value.

Example Usage:

componentWillReceiveProps(nextProps){
if (this.props.post.category !== nextProps.post.category){
this.setState({
title: nextProps.post.title,
body: nextProps.post.body,
category: nextProps.post.category,
})
}
}

I must give a shout out to this article that enlightened me on the solution that dozens of other posts, blogs, and repos failed to mention. Anyone else who has had trouble finding an answer to this evidently obscure problem, Here it is:

ReactJs component lifecycle methods — A deep dive

componentWillReceiveProps is where you'll update state to keep in sync with props updates.

Once state updates, then fields depending on state do re-render !

As I know only thing redux does, on change of store's state is calling componentWillRecieveProps if your component was dependent on mutated state and then you should force your component to update it is like this

1-store State change-2-call(componentWillRecieveProps(()=>{3-component state change}))

This answer is a summary of Brian Vaughn's article entitled You Probably Don't Need Derived State (June 07, 2018).

Deriving state from props is an anti-pattern in all its forms. Including using the older componentWillReceiveProps and the newer getDerivedStateFromProps.

Instead of deriving state from props, consider the following solutions.

Two best practice recommendations

Recommendation 1. Fully controlled component
function EmailInput(props) {
return <input onChange={props.onChange} value={props.email} />;
}
Recommendation 2. Fully uncontrolled component with a key
// parent class
class EmailInput extends Component {
state = { email: this.props.defaultEmail };


handleChange = event => {
this.setState({ email: event.target.value });
};


render() {
return <input onChange={this.handleChange} value={this.state.email} />;
}
}


// child instance
<EmailInput
defaultEmail={this.props.user.email}
key={this.props.user.id}
/>

Two alternatives if, for whatever reason, the recommendations don't work for your situation.

Alternative 1: Reset uncontrolled component with an ID prop
class EmailInput extends Component {
state = {
email: this.props.defaultEmail,
prevPropsUserID: this.props.userID
};


static getDerivedStateFromProps(props, state) {
// Any time the current user changes,
// Reset any parts of state that are tied to that user.
// In this simple example, that's just the email.
if (props.userID !== state.prevPropsUserID) {
return {
prevPropsUserID: props.userID,
email: props.defaultEmail
};
}
return null;
}


// ...
}
Alternative 2: Reset uncontrolled component with an instance method
class EmailInput extends Component {
state = {
email: this.props.defaultEmail
};


resetEmailForNewUser(newEmail) {
this.setState({ email: newEmail });
}


// ...
}