Connect actions to functions - javascript

Recently I've been using react classes and using mapStateToProps and the react-redux connect, i've been able to use actions within my classes because of this, however now I'm trying to write functions that don't belong to a class like the following
import { some_imported_action } from '../actions/my_actions';
export function f() {
some_imported_action();
}
But I'm having trouble trying to get the function to run the action imported, I assume the problem is trying to use the react-redux connect with a function instead of a class. When I run the code above nothing happens, no errors but the action doesn't execute.
I've tried importing prop-types and making the function required with
some_imported_action: PropTypes.func.isRequired
The errors I get from trying to run it are things to do with either 'this', or 'props' is undefined in the line.
this.props.some_imported_action();
I think it might not even be possible without a class, any ideas?
Thanks

When your f() function is called nothing happens with Redux because the action creator (some_imported_action) is not connected to your store.
Redux actions needs to be dispatched using the store.dispatch function in order to be handled by reducers.
The connect function from react-redux receive as second argument a mapDispatchToProps object or function:
[mapDispatchToProps(dispatch, [ownProps]): dispatchProps] (Object or Function):
If an object is passed, each function inside it is assumed to be a
Redux action creator. An object with the same function names, but with
every action creator wrapped into a dispatch call so they may be
invoked directly, will be merged into the component’s props.
If a function is passed, it will be given dispatch as the first parameter. It’s up to you to return an object that somehow uses dispatch to bind action creators in your own way.
Example passing a callback prop called someAction binded with store dispatch:
import { connect } from 'react-redux';
const someAction = () => ({ type: 'SOME_ACTION' });
function MyComponent({ someAction }) {
return <div onClick={someAction}>...</div>;
}
export default connect(null, { someAction })(MyComponent);
Read more info related to React-Redux connect

Related

Need some clarifications on some basic setup

After watching a few Redux tutorials I still have a few questions:
After creating my store, i have:
ReactDOM.render(, document.getElementById('root'));
Going into the App component, I define:
function mapStateToProps(state) {
return {
posts: state.posts,
comments: state.comments
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(actions, dispatch)
}
Where do state and dispatch come from? Is this automatically referring to the store's state and dispatch since i connected it to my component?
One of my React components has a form which on submit, calls a function:
handleSubmit(event) { ... }
So in the constructor of that component, I have:
constructor() {
super()
this.handleSubmit = this.handleSubmit.bind(this);
}
Is calling super() always necessary when declaring a React class component?
Why do I need to do this type of binding there?
After submitting that form, I dispatch an action called addPost.
How does it "go" to the reducer? Is it just because the reducer was given when I created the store and using mapDispatchToProps(dispatch) I "let" Redux know which actions could be dispatched to that reducer?
The reducer simply returns a state object. Where is the logic that actually "saves" that state into the store? Is it hidden?
Yes, treat state and dispatch as references to your redux state and the dispatch function respectively.
React docs say:
If you don’t initialize state and you don’t bind methods, you don’t need to implement a constructor for your React component.
Try an arrow function for your handler:
class MyComponent extends Component {
handleSubmit = () => {
// ...
}
}
The reducer was given to the store, the store was given to the react-redux <Provider> component, and the Provider component provides a React context where dispatched actions in descendant components get picked up by said reducers.
That logic is in the redux library.
Please check out our new official React-Redux docs . Specifically, you should read through connect: Extracting Data with mapStateToProps and connect: Dispatching Actions with mapDispatchToProps.
If you define a constructor for a class, and it extends another class, then yes, you need to call super() as the first line of the constructor - that's how ES6 classes are defined to work.
Redux only has a single reducer function. The insides of dispatch() look like this:
function dispatch(action) {
newState = rootReducerFunction(oldState, action);
subscriberCallbacks.forEach(callback => callback() );
}
You might want to read through the Structuring Reducers page in the Redux docs to get a better understanding of how and why reducers are normally split into smaller functions.
In most cases, the logic that combines the different "state slices" back together is in combineReducers(), because you used it to generate the "root reducer" function.
Where do state and dispatch come from? Is this automatically referring to the store's state and dispatch since i connected it to my component?
State is one of the hardest to understand topics and comes in front and center when we start concentrating on Redux. Each class-based component that we define has its own state object.
mapStateToProps is our ability to interface from the application level state down to the component level. Its where we sort of pluck properties off our state object and inject them into our components.
dispatch is how you change the state in your application, you dispatch an action.
In your implementation, you are using bindActionCreators, which turns an object whose values are action creators, into an object with the same keys, but with every action creator wrapped into a dispatch call so they may be invoked directly.
I personally have not ever connected redux actions this way, so I had to look this one up.
Is calling super() always necessary when declaring a React class component? Why do i need to do this type of binding there?
Yes, React components always have to call super in their constructors to be set up properly.
For an in-depth explanation of this kind of binding trickery, begin with the React docs. If you are not so inclined, just know that in React, whenever you define an event handler that uses this, you need to add this.methodName = this.methodName.bind(this) to your constructor function.

Redux-form's dispatch and typescript

Currently came through an issue with properly typing dispatch function in redux-form.
onSubmit function from redux-form accepts two arguments:
const onSubmit = (values, dispatch) => {...};
However, it's not the exact same dispatch as this one from redux.
I mean, it looses typings. I can type it anyway I want and typescript wont throw any errors, e.g. ..., dispatch: Dispatch<{ foo: 'bar' }>) even if there's no foo key anywhere in my component.
Dispatch type comes from redux and works fine if using dispatch with connect, e.g. in mapDispatchToProps function.
Q: How to type the dispatch function in redux-form properly?
Important:
When going to Dispatch initialization place, in index.d.ts file in redux node package, there's following interface:
export interface Dispatch<S> {
<A extends Action>(action: A): A;
}
Why S is being omitted? Is it the reason why if I put anything as an argument for Dispatch, typescript accepts it?
Thanks!

connect method redux arguments

The following piece of syntax when dealing with redux's connect method is confusing me.
export default connect(
mapStateToProps,
mapDispatchToProps
)(TodoItem)
connect takes two arguments, mapStateToProps, mapDispatchToProps, which makes sense. The same as any function i.e.
function sum(a, b){
return a + b;
}
sum(2, 8);
However, what is confusing me is the the next argument, in the example I provided above (TodoItem). Can anyone explain what is happening here?
You need to export a component from your JSX. The connect function is a higher order function, meaning it returns a new function.
The new function that it returns is actually a higher order component. This means that it is a component that returns another component. In this case, it is the Provider component that wraps your component.
At this point:
connect(mapStateToProps, mapDispatchToProps)
you've not actually told the connect function which component to wrap.
connect(mapStateToProps, mapDispatchToProps)(TodoItem)
But here you've also told it which component to wrap.
Summary
So the first two functions you pass in as arguments tell it what the component needs. The last function argument tells it which component it is that needs them.
This is function currying.
In general, this is a function that returns a function.
In the connect the use case is to pass the component as an argument to the "second" inner function and that way it will have access for all 3 parameters.
One simple use case is when you want to pass extra parameter to onClick event.
var root = document.getElementById('root');
root.addEventListener('click', clicked('i Was clicked!!'));
function clicked(param){
return function(e){
// both parameters acceesible
console.log(param);
console.log(e.target.innerHTML);
}
}
<button id="root">Click me</button>
So behind the scene it invoked like this:
clicked('i was clicked!!')(event)

dispatch is not a function TypeError

I'm new to react-redux and I'm using the react-thunk middleware. I'm getting the error "Uncaught TypeError: dispatch is not a function" whenever I try to run my action creator (by pressing my button). Anyone know what's going on?
src/actions/index.js
function editUserName (newUserName) {
return {
type: 'CHANGE_USER_NAME',
newUserName: newUserName
}
}
export function editUserNameDelegate () {
return function (dispatch, getState) {
return dispatch(editUserName("thunkUser"))
}
}
src/containers/admin.js
import { editUserNameDelegate } from '../actions/index'
...
<input type="button" onClick={ editUserNameDelegate() } value="edit UserName" />
You're executing the editUserNameDelegate function inside of render, instead of passing it as the onClick handler. You want onClick={editUserNameDelegate} instead.
Also, if you're writing it that way, you need to make sure that the function is bound up to dispatch when you call it. You can do that like:
export default connect(null, {editUserNameDelegate})(MyComponent)
And then pass onClick={this.props.editUserNameDelegate} in your render function.
The store is what provides the dispatch function. So you either need to connect your component to the store using something like connect from react-redux (https://github.com/reactjs/react-redux), or you need to call store.dispatch(editUserNameDelegate()).
You also need to make sure you have added redux-thunk as middleware in your store. See more about redux-thunk here: https://github.com/gaearon/redux-thunk
You should provide what is src/containers/admin.js function or class?
If it is function component you should use onClick={() => editUserNameDelegate()}
If This Error Is As A Result Of Using context you might consider importing it as:
const {state, dispatch} = useContext(location of the context)

React/Redux, implementing multiple actions with Redux Thunk

I am learning a react/redux and have an application with two main pieces of state:
An array of items
An object that contains user-specified filters for those items
I have three functions/actions, createFilter, updateFilter, and deleteFilter that modify the state of #2. I have an action filterItems that modifies #1 based on the state of #2. So whenever #2 changes, this action needs to be dispatched.
This is the component I am working with:
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { createFilter } from '../actions/actions'
import { updateFilter } from '../actions/actions'
import { deleteFilter } from '../actions/actions'
import { filterItems } from '../actions/actions'
class ItemList extends Component {
createFilter(input) {
this.props.createFilter(input)
this.props.filterItems()
}
updateFilter(input) {
this.props.updateFilter(input)
this.props.filterItems()
}
deleteFilter() {
this.props.deleteFilter()
this.props.filterItems()
}
...
// Render method
...
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ createFilter, updateFilter, deleteFilter, filterItems }, dispatch)
}
function mapStateToProps({ itemList }) {
return { itemList }
}
export default connect(mapStateToProps, mapDispatchToProps)(ItemList)
What I have found is that when one of the filter methods are sent, the store (state #2) is not yet updated by the time filterItems() is called.
So I need to asynchronously execute the filter functions, and once the store is updated call filterItems.
I am struggling on how to do this with react-thunk. If the first function was an ajax promise I would use .then():
export function updateFilterAndEvaluate(input) {
return (dispatch, getState) => {
updateFilter(input).then(dispatch(filterItems(getState().filters)))
}
}
But these are just functions, and don't have a .then() method. I am trying to figure out what my best course of action is for this implementation. Can I wrap Redux actions in a promise? Am I misusing Thunk? Or should I attempt a different pattern entirely?
I have an action filterItems that modifies #1 based on the state of #2.
This is, generally speaking, an anti-pattern. Since the result array can be computed from the source array and the currently active filters, you shouldn’t be keeping it in the state.
Redux actions should generally look like “events” (e.g. what happened). “Filter was created” and “filter was updated” are good actions. “Filter them now!” looks more like a command, this is usually a sign that it shouldn’t have been an action in the first place, and should be something the components do as they select the data to render.
Instead, do the filtering as part of your mapStateToProps() function when you prepare data for the components. If it gets expensive, look into using Reselect to compute derived data efficiently.
As for your specific question,
What I have found is that when one of the filter methods are sent, the store (state #2) is not yet updated by the time filterItems() is called.
This is incorrect and indicates some other problem in your code. (It’s hard to tell where because the example is incomplete). In Redux, dispatch() is synchronous (unless you have some middleware that delays or batches it which usually isn’t the case), so you don’t need to “wait” for it to finish if it just operates on the local data.
However, in any case, filterItems() is not a very good fit for an action, and I suggest you to look into filtering in mapStateToProps() as I wrote above.

Categories

Resources