state in react with redux - reactjs

I have 3 questions about state in react with redux
the first one is that, as I know, state is an object. But from where can I know the fields in the state object?
the second one is that, from the todomvc official example, there is one state:
state = {
editing: false
}
handleDoubleClick = () => {
this.setState({ editing: true })
}
in my understanding, setState would update the global state. so does that mean this state is global not local? and the state only has one field editing?
state = {
editing: false
}
my last question is that, currently I am using angular to develop my website, which has 3 pages, e.g. www.example.com/invoice, www.example.com/customers, and www.example.com/suppliers. for each page, I would use angular to fetch data from backend. so if I want to rewrite it in react with redux, the state should be something like below?
{
type: xxx,
invoiceData: xxx,
customerData: xxx,
supplierData: xxx,
//some other cool stuff
}

Usually, if you want to change or access the state, you have previously defined its fields. That's what you do with state = {editing: false}.
I think that you are mixing react's state with Redux. The state of Redux is global to the app, but in this example you are not using Redux. This state is React's component state, so it's only accessible locally in the component. setState is a React function that asynchronously mutates the state of the component. After setState is called, the component is re-rendered.
I think that this question is unclear because, as I said, you might be confused with React and Redux. Try to understand React by itself first and then take a look at Redux.

Related

Accessing redux state in ComponentDidMount

I'm facing an issue with Redux and React
I use a redux action to fetch data from an API. When the component mounts, this action is fired and populate the Redux state. I want a second action to be fired with parameters (article) from the redux state.
My issue is that when I fire the second action, the redux state is still empty, so article is null, which causes an error.
componentDidMount() {
const { targetArticle, userVisit, match, article } = this.props
targetArticle(match.params.slug);
userVisit(match.params.slug, article.title);
}
I've already checked other topics on the subject like this one, but none of them works for me. How can I achieve that?
Thanks
You'd probably have to use componentDidUpdate lifecycle method. So given that userVisit is dependent on the result of targetArticle and assuming you are looking to this.props. for the updated Redux state, something like this should get you there:
componentDidUpdate(prevProps) {
if(prevProps.article !== this.props.article) {
// Now you have access to targetArticle's result and updated Redux state
userVisit(match.params.slug, this.props.article.title)
}
}
More in the docs: https://reactjs.org/docs/react-component.html#componentdidupdate

Is the storage of data acquired, with an HTTP-Client such as Axios, in the context-api state a good practice?

i've been developing an app using react 16.8 and to avoid importing Axios in every component i started making use of the context API to hold data from all my requests on its internal state and reference this data in the component where i need it with the <Consumer/>, but the number of properties in the state grew considerably, i wonder if this is ok, and if it is should i clean the state after using the data where i needed, in a method like ComponentWillUnmount?
Class ContextProvider {
state = {
prop1: [],
prop2: [],
prop3: [],
{...n}
}
getProp1Data = () => {
Axios.get(url)
.then(res => this.setState({prop1: res.data})
}
{.... n}
}
You should consider using Redux since with Redux you can travel between the state history in development, and re-evaluate the current state from the action history when the code changes. In this way you be able to import Axios only once to your middle ware and to make your code more clear.
In case you consider to use ComponentWillUnmount you won't be 'cleaning' your state since setState() can't be called on this life cycle method. ComponentWillUnmount is related to the competent itself on the DOM and not to the state. There's not a problem with your props being 'grew considerably' and you may use as many as you like.

Updating State with componentWillReceiveProps

I am looking at someone else code who updated the state of object in the react lifecyle:componentWillReceiveProps. I am fairly new to react and redux but thought that you do all the updating of state in the reducer useless its local state. Can someone tell me why he is doing it in componentWillReceiveProps? Thanks
componentWillReceiveProps(nextProps) {
if(this.props.isEditingbook && !nextProps.isEditingbook) {
let book = this.props.index.bookDictionary[this.props.currentbook.id]
book.name = this.state.model.name.value
}
this.setState({ ...this.state, ...nextProps})
}
Well, first of all, componentWillrecieveProps has been deprecated because it might cause some problems, take a look here . Instead, React docs point out that you should use componentDidUpdate which is a safe-to-use method.
And answering your question, if you looked a code where that person was using redux, then he used that deprecated method because when you bind a component to redux goblal state (store) through mapStateToProps, it's properties are bind to that component props. So, in other words, whenever the global state changes so does the component props, and if you want to "track" these changes in your component logic, you have to know when it's props are going to change, that's why you use componentWillRecieveProps or componentDidUpdate methods.
Here is how that example code should has been done with componentDidUpdate:
componentDidUpdate(prevProps) { //prevProps is the previous props of the component before being updated
//so, if this.props != prevProps it means that component props have been updated
if(this.props.isEditingbook && !prevProps.isEditingbook) {
let book = this.props.index.bookDictionary[this.props.currentbook.id]
book.name = this.state.model.name.value
}
this.setState({ ...this.state, ...prevProps})
}

How does a redux connected component know when to re-render?

I'm probably missing something very obvious and would like to clear myself.
Here's my understanding.
In a naive react component, we have states & props. Updating state with setState re-renders the entire component. props are mostly read only and updating them doesn't make sense.
In a react component that subscribes to a redux store, via something like store.subscribe(render), it obviously re-renders for every time store is updated.
react-redux has a helper connect() that injects part of the state tree (that is of interest to the component) and actionCreators as props to the component, usually via something like
const TodoListComponent = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)
But with the understanding that a setState is essential for the TodoListComponent to react to redux state tree change(re-render), I can't find any state or setState related code in the TodoList component file. It reads something like this:
const TodoList = ({ todos, onTodoClick }) => (
<ul>
{todos.map(todo =>
<Todo
key={todo.id}
{...todo}
onClick={() => onTodoClick(todo.id)}
/>
)}
</ul>
)
Can someone point me in the right direction as to what I am missing?
P.S I'm following the todo list example bundled with the redux package.
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 !
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 });
}
// ...
}
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}))

Reusable react-redux container components

In my React/Redux app I am often facing with the problem of implementing components with state which should be used throughout the app.
Let's take simple popup component as an example with open/close state which can be reused in any page.
Here is two possible approaches I found:
Use setState and "local" reducer (I use recompose.withReducer which is just syntax sugar for React's native setState) to manage its state. It looks easy and reusable until you need change the component's state in the other part of your page (close popup in out case). And you cannot just call some redux action to change the state.
Keep the component's state in the Redux store. With such approach we can call closePopupAction({ id }) in any place of the components tree to change it's state.` But we need somehow put its reducer (which i want to keep in the popup's folder) to the root reducer when the component is mounted and delete it when the component is unmounted. Plus we can have multiples popups in the page and each of them have its own state.
Did anybody face with a similar problem ?
I think you should keep state of component in redux. You can create reducer for this component and use combineReducers function in this way:
rootReducer = combineReducers({
moduleA: combineReducers({
popupA: popupReducer("id1"),
popupB: popupReducer("id2")
}),
moduleB: combineReducers({
popupA: popupReducer("id3")
})
})
});
then when you call closePopupAction("id1") reducer can check id and change proper part of state.
PS: Id should be provided in better way :)
You could mount each component's state to its own slice of the store.
So your closePopupAction actions would be called with that mount path:
closePopupAction({ mountPath: 'popups.popup1' })
and you would need only one reducer to handle all closePopupAction actions, which could be registered once at startup:
(state, { type, mountPath }) => {
if (type === 'CLOSE_POPUP_ACTION') {
// manipulate with the slice at `mountPath`, e.g.
return _.set(_.cloneDeep(state), `${mountPath}.isOpen`, false)
}
// ...
}

Resources