Redux - Set state by depending actions - reactjs

I want to achieve the follow sequence of my data flow:
Get the record-id of react-router
Set this id as state property
If this property is set, all other dependent components, with it's own reducer and actions should get their data depending on the id
componentWillMount() {
this.props.actions.setItemId(this.props.params.id);
if(this.props.id){
this.props.fetchItemInformations(this.props.id);
}
}
I am aware that this flow won't work but I've no idea how to implement it.

Many ways to do this but if your problem is due to async issues then you can use middleware such as Redux Thunk. This will essentially allow you to chain your actions i.e. once the id is set then dispatch another action to do X.
See here - https://github.com/gaearon/redux-thunk
Alternatively, you could do your if statement within the componentWillReceiveProps () lifecycle event and check if it has been set there. If it has then dispatch your next action...

If I understand your question right, you want to fetch the property record-id from react-router (This is a url hash I assume?).
You can directly pass the this prop to all the children (as a prop) and handle the value in the children as you wish. To do this,
Pass the prop as well when rendering { this.props.children }; something like
{ React.cloneElement(this.props.children, { record-id: record-id }) }
This I'd say is the best practice; if you want to pass the record-id to other components (that are not direct children) of your parent component; you need to fire an action and handle the action to update relevant reducer states. Hope that helps.

Related

Putting props to state in react

I'm working with react-redux in a current project and on my search for react wisdom I came across a 'convention' I don't understand.
Other programmers tend to put a prop to state right in the constructor. Why is that?
I've never seen it in the official documentation and from there I learned that these two are two different things to hold and share data.
Is there any use in it or mybe just a personal preference?
Thanks.
It sounds like a pattern for when you need some initial value from outside the component, but then want to either ignore, or not immediately affect, the outside (by for example dispatching Redux actions on each value change).
For example, maybe you have a form that should be prefilled with some values that the component gets from the outside (Redux state) through mapStateToProps or similar. You could let every form fields onChange dispatch an action that changes the Redux state, causes the incoming props to change, and then re-render the form with the new value. Or maybe you find that to be overkill. Maybe you're satisfied with keeping the changing form data in the component internal state until the user actually submits the form, and only then dispatch an action that will post the form data, change the Redux state and pass new props (like success status) down to the form component.
Yes, it is possible, especially when you want to keep things simple by not using container components or flux / redux store to manage application's state.
A component can manage its own state, and the initialState will be assigned as the props passed from its parent component.
Consider the following example:
class TodoList extends React.Component {
constructor(props) {
// Assign todos property to state so that the TodoList component
// can self-manage this value without interacting with outside components.
this.setState({ todos: props.todos });
}
...
addTodo(todoDescription) {
this.setState({ todos: this.state.todos.concat[todoDescription] });
}
}
However, I still do recommend to separate the view components and data manipulating components when your applications is complex.

How to update store state in react-redux?

I'm working on a component in React (with Redux) that has the requirements to:
Retreive data from an API endpoint.
Display that data, with one field being editable.
If the editable field is edited, it has to update the rest of the fields and send them to a different endpoint.
I'm having trouble grasping how exactly I should implement the editing using the Redux metodology.
Here is a simplified component:
class MyComponent extends Component {
constructor(...args) {
super(...args);
this.props.dispatch(getDataFromEndpoint());
}
editField(e) {
this.props.dispatch(editFieldAction(e.target.value));
}
render() {
return (
<div>
<input type="text" defaultValue={this.props.data.editableField} onChange={this.editField} />
{
this.props.data.uneditable.map(x => {
return (
<span>{x}</span>
);
})
}
</div>
);
}
}
const mapProps = state => {
const { data } = state.endpointData;
return { data };
}
export default connect(mapProps)(MyComponent);
Note that I've written this component as an example, it might have errors, but you should get the point. Whenever this.editField is called, it should update every value in this.props.data.uneditable.
What is the correct way to do this?
In a sense these questions all boil down to: How do I wire up my component(s) to read from (and write to) Redux state. This is essentially the purpose of redux-react's connect function (which you have in the bottom of your example). You use the first parameter, mapStateToProps, to be able to read from state. You use the second parameter (that you don't have yet) - mapDispatchToProps - to provide the means to write to it. Dispatching an action is how you update Redux state.
You can simply use this.props.dispatch, but it is more idiomatic to use mapDispatchToProps, which simply provides an object where each property is a function which calls dispatch with the appropriate action creator. You might accept as a parameter for example the data which you are looking to update your store with.
What ends up happening is in your component when you want to update the state (say from an input), you use the methods you wired up with mapDispatchToProps to dispatch an action with the relevant data (the updated input value), and then write a reducer which responds to the action and updates the relevant part of your store. Redux will then trigger a re-render in your component and it will pull the updated data from the store.
Async actions are handled a bit differently of course (which is what your API calls will be), because they typically have several changes to state being spread out over time. Usually something like:
Start the API request. (Dispatch an action to..) Set a flag somewhere in your state indicating the API request is 'in transit'.
Receive the response. (Dispatch again to..) Set a flag somewhere saying the request is finished, and if it was successful, store your response data somewhere. If it was an error, store the error (or just the fact there was an error).
I would recommend redux-thunk for this purpose, since at least for me it is the easiest to understand and work with out of all the various libraries and methods to handle async with redux. Instead of dispatching an object which contains the data describing the action, you instead dispatch a function, which means you can write any kind of logic you would like, allowing you to dispatch several times for example. This makes it easy to do the steps outlined above.. simply have a function which calls your API, and in the callback (or .then() method) you dispatch an action with the result.

Correct way to use API with React/Redux

I'm a bit confused about the component lifecycle in React. Consider a component that loads its initial state by dispatching the action, getTodo() and updating the state of the entire app
componentDidMount() {
var _this = this;
axios.get('/api/todo').then(function(response) {
_this.props.actions.getTodo(response.data);
}).catch(function(err) {
console.log(err);
});
}
What can I use in componentWillUnmount() to prevent the memory leak? If I choose to do this, how will it be possible to update the state when I come back to the page from another page with this method?
Also, is it a better to just store the props that I need for the page as the state and updating the state instead? My concern with this approach is that it just doesn't update the state of the entire app and other components that might need the same props will have to go through the same unnecessary process, which could be avoided by using the first approach.
You should avoid doing api call in a component. React offers an interface "nothing" more.
You should dispatch an action. In fact an action creator can be used (check redux-thunk or a redux-api-middleware). Your state app must be hold by redux store, not in your interface.
You probably have a "top" component will is mount only once, it can dispatch this action (action which is able to get initial state)
I hope it will help

Redux action creators state access best practices

In redux, when we need to access a state property that is not directly related to a componentProps but is triggered from this component (ie: a grand parent component id that is stored in the state) should we try to pass this part of the state to action creators from the component that triggers the event ie:
onClick={() => doSomething(grandParentId)}
----
function doSomething(grandParentId) {
console.log(grandParentId)
}
or should we get the data from getState with redux-thunk?
onClick={doSomething}
----
function doSomething() {
return (dispatch, getState) => {
console.log(getState().grandParentId)
}
}
I know that the answer sounds like the first approach is more proper, but I somehow feel that getState with redux-thunk can lead to bad practices and I would like to know when I should use it or not.
If you already have that information in your component, I don't see why not passing it along to your action creator. The flow of data is much easier to follow this way, in my opinion.
I would take advantage of getState() only when I need to access part of the state tree that I wasn't able to pass via action creator.
If the data is already in the redux state, then I would access it with getState. Fewer arguments being passed around makes it simpler IMO.

Passing actions down the stack via props

I have been using Redux for a few weeks now and I am very happy with it and I am getting used to a Redux way. I am using it with React. Still plenty to learn as both things are new to me.
I have a one problem - maybe I am doing something wrong ... Let me show you:
I have a component structure that looks like this:
App //root of the application aka smart component
CampaignTable
CampaignHeaderRow
CampaignHeader
CampaignDataRow
CampaignData
The App component is initialized as(only related code):
import * as DashboardActions from '../actions.js'
function select(state){
return {
campaigns: state.campaigns, // array of campaign objects, has name, id, time created etc
order: state.order // sort format "byWhichField"
// will affect the way how campaigns are displayed
}
}
function mapDispatchToProps(dispatch){
return bindActionCreators(DashboardActions, dispatch)
}
export default connect(select, mapDispatchToProps)(App);
App has now access to state and all actions as props.
The problem I see with it right now is:
I would like CampaignHeader to fire the action that will change the state.order state. Let say I will make <th>Text</th> inside CampaignHeader clickable. This will fire the action to change state.order which will in turn affect campaigns order on a next rerender.
So I have my action available inside App props. To pass it down to
CampaignHeader I would have to:
pass it down to CampaignHeader as props
assign it to variable inside CampaignHeader and pass it down as props to CampaignHeaderRow
assign it to variable inside CampaignHeaderRow and pass it down as props to CampaignHeader
assign it to variable inside CampaignHeader and fire the action inside onClick event....
This is a lot of boilerplate, assignments and bag passing! Just to get action fired.
All the components along the way are aware of this action.
When I decided to implement this feature I have opened CampaignHeader component file. I have added the logic and called the action, I have added the action to action file. All I needed is to get a props set. CampaignHeader component doesn't hold a reference to its parent so I didn't know straight away where should this props be injected from(in this example is obvious but I hope you get a point).
What if I will have even deeper component structure?
Is this approach correct?
Could I tackle this problem differently?
UPDATE:
As #Errorpro suggested will it be ok to connect single action and state.order to CampaignHeader?
Worried about: If I will do it once I will be doing it all the time.
There's a discussion in the issue-section of the Redux github repo about wether it's okay to use multiple connects or if everything should be passed down from the top through props, and in there Dan Abramov (the creator of Redux say's:
[...]
Nobody advocates a single connect.
[...]
The "single" only refers to small apps like the one we create in the
example. Please feel free to amend the docs to better clarify this. I
am now busy with other projects so please don't expect this issue to
get any movement unless somebody makes a PR. You can do it too.
The comment probably makes more sense in context though so check out the entire issue thread https://github.com/rackt/redux/issues/419#issuecomment-140782462
If you use redux you should know about dumb and smart component. So we use this sctructure:
component
index.js
Component.js
ComponentContainer.js
Dumb component just get props and render it. More interesting in smart component. Here it is:
export default compose(
relay({
fragments: {
viewer: () => Relay.QL`
fragment on Viewer {
any data from relay
}
`,
},
}),
connect(
null,
{
onCreate: createUserAction,
},
(stateProps, actionProps, parentProps) => ({
...parentProps,
onCreate={() => actionProps.onCreate('user')},
})
),
)(Component);
So, parentProps and onCreate function will be in dumb component's props. There you can use this.props.onCreate and invoke it or pass it farther.
Passing the actions - like any other props - from parent to child to grandchild etc is the idiomatic React way. In my opinion your approach is correct; even if it feels wrong.
But there are a couple of alternatives.
There is a feature in React called context. Context permits the passing of fields from a higher order component to a lower order component whilst skipping the middlemen. However, it's an experimental feature so I would recommend avoiding it for now. https://facebook.github.io/react/docs/context.html
Additionally, there is a Redux specific way where you can make any lower order node of your choosing a "smart component" (in the Redux sense). That is, you wrap your class export in the connect function to plug it directly to the store, in the exact same way you do for the Root node.
Personally I tend to stick to the top-down way. There may be a fair bit of boilerplate involved but at least it means your application is easy to reason about.

Resources