react redux pass this.props to actions from component - reactjs

In my component class I have this.props which contains history object which contains transitionTo function.
render() {
console.log(this.props)
return (
<div>
<button onClick={actions.myAction}>My button</button>
</div>
Here my button is calling myAction which when completes wants to route to some other location. I want to access this transitionTo function in my action and performs something like router.transitionTo("/page1")
For this I should get history object which contains transitionTo function. So how can I pass this.props.history object to actions. When I do onClick={actions.myAction(this.props.history)} my button is not being rendered..
Any help

The answers above are correct, but fall appart when migrating to "Stateless Components" this quickly falls apart. My suggestion is using a higher-order function to take care of both the handling of preventing default click action and supplying the correct arguments. If you're already using ES2015 (through Babel for example), this could be this simple:
const preventDefault = (fn, ...args) => (e) => {
e.preventDefault();
fn(...args);
};
Then in your components call like so:
const Item = ({
item,
openItem
}) => (
<div>
<h1>{item.title}</h1>
<a href="#" onClick={preventDefault(openItem, item)}>Open Item</a> // on click will call `openItem(item)`.
</div>
)

I do not agree with your approach, but find bellow the solution to your problem:
handleBtnClick() {
actions.MyAction(this.props.history);
}
render() {
return (
<div>
<button onClick={ this.handleBtnClick.bind(this) }>My button</button>
</div>
);
}
When your button is clicked the function handleBtnClick() is invoked and there you can call your action and pass any props you want.
onClick expects a function expression (something that evaluates to a function expression). You are not supposed to make complex calls in there such as actions.myAction(this.props.history).
I would suggest you to consider using react-router-redux and handle url transitions using routeActions and custom redux middleware. I can expand this approach if you are interested.
EDIT:
If you use react-router-redux then you should take advantage of methods in routeActions. The method you need in this case is push(). So, if you want to transition to the /user/account url you just call dispatch(routeActions.push('/user/account')) and react-router-redux will handle the transition.
Eg:
import { routeActions } from 'react-router-redux';
// Action creator
function mySuperAction(flag) {
return (dispatch, getState) => {
dispatch(routeActions->push('/user/account'));
}
});
You need to have your store properly setup with react-router-redux in order for this to work.
Or from a component:
import { routeActions } from 'react-router-redux';
import { connect } from 'react-redux';
import React from 'react';
class MyComponent extends React.Component {
handleClick() {
this.props.dispatch(routeActions.push('/user/account'));
}
render() {
return (
<div>
<button onClick={ this.handleClick.bind(this) }>My button</button>
</div>
);
}
}
export default connect()(MyComponent);

Related

Will the background component unmount when modal is open in react? If not, how to maintain/store states of modal and react component using redux?

Demo ( not working but for file structure)
https://codesandbox.io/s/material-demo-bsr06
I need to open a Modal when I clicked something in this URL dashboard/someMails, it should open with dashboard/someMails/:id.
I want to maintain the states of someMmails pages' one of the emails and that particular email's modal and visa versa
In React router I switch
<Switch>
<Route path="dashboard/someMails" component={EmailsHome} />
<Route path="dashboard/someMails/:id" component={EmailModal} />
</Switch>`
Since I need to maintain states, storing states in reducer(redux).
But when to store is the question.
a) Can I store when componentUnMounts?
b) Will the component unmount when modal is open?
c) If component will not unmount should I keep triggering reducer method to store each and every time state change in EmailsHome or EmailModal?
The component will unmount when you navigate to the modal route. That being said, you should avoid executing logic inside componentWillUnmount() other than clean-up methods.
It looks like you want to update some state in redux when a user clicks on a email, causing them to navigate to different route and open a modal.
If that is the case, you should trigger this action in the onClick() handler of the Link that sends them to another page.
I've created a basic template on how you can achieve this feature: https://codesandbox.io/s/material-demo-wpisp
The integral pieces are here:
Email.js
import React, { useState } from "react";
import { Link } from "react-router-dom";
import { connect } from "react-redux";
import { updateEmail } from "./emailActions";
const Email = props => {
const [checked, setChecked] = useState(props.read);
return (
<div>
<button
style={{ background: "transparent", border: "none" }}
onClick={() => props.updateEmail(props.id, checked)}
>
<Link to={`/dashboard/someMails/${props.id}`}>{props.message}</Link>
</button>
<button onClick={() => setChecked(!checked)}>Mark As Read</button>
<p>{checked ? "Read" : "Unread"}</p>
</div>
);
};
const mapDispatchToProps = dispatch => {
return {
updateEmail: (id, value) => {
dispatch(updateEmail(id, value));
}
};
};
export default connect(
null,
mapDispatchToProps
)(Email);
When you click "Mark As Read" that will toggle the "checked" state
of the Email component.
Follow-up by clicking the link, it will call our action-creator,
which will pass in the id of the message and the state-value
(true or false).
Redux, which is holding our array of messages, updates the message
with matching id, and it gets the state-value we configured inside
our component. Setting the "read" value to be (true or false).
EmailModal.js
import React, { useEffect } from "react";
import { connect } from "react-redux";
import { getEmail } from "/emailActions";
import { Link } from "react-router-dom";
const EmailModal = ({ emails, getEmail, match }) => {
useEffect(() => {
const id = match.params.id;
getEmail(id);
}, []);
return (
<div>
<Link to="/dashboard/someMails">Back to emails</Link>
<h4>Message: {emails.currentEmail.message}</h4>
<h4>Read:{"" + emails.currentEmail.read}</h4>
</div>
);
};
const mapStateToProps = state => {
return {
emails: state.emails
};
};
const mapDispatchToProps = dispatch => {
return {
getEmail: id => {
dispatch(getEmail(id));
}
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(EmailModal);
The component mounts, we call an action-creator to find the email
in Redux with the same id of the one in our url.
We display that email, and now depending on whether or not you
changed the state of the email in the previous page (read or
unread), it will get that updated value and display it in the modal.
To summarize, yes you can execute redux-logic while React is in the process of navigating to another Route. In this, we passed some component state-value to an action-creator, it updated redux-state and when we finally navigated to the modal component, it got the updated value.

PureComponent keeps rendering even though no state/prop changes

I'm trying to learn and test React.PureComponent and it keeps rendering even though no state changes for that pure component.
My PureComponent is very simple and it accepts only one Redux Action function via connect hoc
import React from 'react';
import {
Container,
Button
} from 'reactstrap'
import { connect } from 'react-redux'
import { resetWorkouts } from '../actions/workoutApiActions'
class About extends React.PureComponent {
render () {
const { resetWorkouts } = this.props;
console.log('in about render...')
return (
<React.Fragment>
<Container>
<h2>Api Data Reset</h2>
<Button color="danger" onClick={resetWorkouts}>Reset Data</Button>
</Container>
</React.Fragment>
);
}
}
const mapDispatchToProps = dispatch => {
return {
resetWorkouts: () => dispatch(resetWorkouts())
}
}
export default connect(null, mapDispatchToProps)(About);
In the above code, you can see that, there is no state in the component. It only accepts the action function as props from connect. However, whenever I clicks on the Reset Data button, it keeps calling the render method as shown in the screenshot.
In the screenshot, I can see that, global state store has been changed whenever, I click a button. But, that state is not used in my PureComponent and it should be out of the scope and my component should ignore to re-render.
Or Redux Action functions are created every time the global state store has been changed. And passed as a new object to my PureComponent ?
Theoretically, I don't need to write my own shouldComponentUpdate function, right? I'm confused and could you please help me to understand about this behaviour?
My goal is I don't want my PureComponent to render again when a user clicks a button.
Updates:
I have tried like the following according to this article and it's still re-rendering
const mapDispatchToProps = {
resetWorkouts
};
this because react do a shallow comparison between the prevProps and the nextProps,
and you can control that only in the shouldComponentUpdate, react doesn't know that the dispatcher is the same one from the previous render, because you are using return inside the mapDispatchToProps function.
In your component and in your case, while the function will remain the same, you can go with two paths:
path 1:
override the shouldComponentUpdate life cycle hook, to be as the following:
shouldComponentUpdate(){
return false;
}
path 2:
get rid of the return inside mapDispatchToProps and simplify the connect so it be as the following:
`conncect(state => ({}), {
resetWorkouts: resetWorkouts})(YourComponent);`
using one of the above paths should make you good to go
The reason why your component is rendering is because everytime the following function executes:
const mapDispatchToProps = dispatch => {
return {
resetWorkouts: () => dispatch(resetWorkouts())
}
}
your components receives a new instance of a property named resetWorkouts(because you're creating an inline array function). You may look at the ownProps to check if your component already have the resetWorkouts:
const mapDispatchToProps = (dispatch, ownProps) => {
return {
resetWorkouts: ownProps.resetWorkouts || () => dispatch(resetWorkouts())
}
}

In a React Component, where should I bind arguments to my onClick functions?

So in an example component
<Button doStuff={doStuff} />
I know this is bad, it creates a new function on every render:
class Button extends Component {
const value = getClickValue(this.props); // do something with props
render() {
<button onClick={() => this.props.doStuff(value)}>Click me</button>
}
}
But not sure which is better for performance from the following:
1) Bind click value in a class property function
This is a trivial example, but in my current component the code to process the props is cluttering my component up and makes the component more smart and less dumb, when you want to strive to have DUMB components and smart containers.
class Button extends Component {
handleClick = () => {
const value = getClickValue(this.props); // do something with props to get value
this.props.doStuff(value);
}
render() {
<button onClick={this.handleClick}>Click me</button>
}
}
2) Send bound functions as props from a container to a stateless functional component.
This method seems better from an organizational perspective. The component is as dumb as possible and is only concerned with rendering. But does it impact performance? As in, is a new function constructed every time the ownProps changes?
const Button = (onClick) => (
<button onClick={this.props.onClick}>Click me!</button>
)
import { connect } from 'react-redux';
const mapDispatchToProps = (dispatch, ownProps) => {
const value = getClickValue(ownProps);
return {
onClick: () => doStuff(value)
};
};
connect(null, mapDispatchToprops)(Button);
In your second approach, every time your props change, mapDispatchToProps is called. Since you're returning an arrow function, it will create that function again.
That makes your first suggestion the best option.

How do I dispatch an action from a Container method in Redux?

I'm new to Redux and I have this question that I can't solve by myself.
I created a TODO list app and now I need to add a new task to the list. The user writes something in a text input and when he clicks the button the new task must be appended to the list.
I already get a 'ADD TASK' action. But I don't know how to call it from inside a component method. So I added an event listener to the button, but when I click it what should I do next?
class List extends React.Component{
addTask(e){
var title = $('[name=title]');
title.val('');
//what should I do?
}
render(){
const mappedTasks = this.props.tasks.map(function(task){
return (
<li key={task.title}>{task.title}</li>
);
});
return (
<div>
<ul>
{mappedTasks}
</ul>
<input name='title' />
<button onClick={this.addTask}>Adicionar</button>
</div>
);
};
}
const mapStateToProps = function(state,ownProps){
return {
message: 'Hello World!',
tasks: state.tasks
}
}
const ListComponent = connect(
mapStateToProps
)(List);
In this example I want to dispatch something from the addTask method.
So what is the proper way of dispatching from a Component method on Redux?
#edit this did the trick:
In render function
<button onClick={this.addTask.bind(this)}>Adicionar</button>
So as Brandon said I could use in the method
this.props.dispatch({type:'ADD_TASK', payload: {title: title.value}});
I see a couple of possible solutions, and which one is more appropriate might depend on your particular use case. There are a few questions that might help:
Is this List component connect-ed to the redux store (using the connect() decorator from the react-redux package? If so, you
should be able to dispatch an action as simply as this:
this.props.dispatch(someActionCreator()).
Are you open to connect-ing this component to the store? If so, decorate the List component like so: connect(mapStateToProps, mapDispatchToProps)(List). Then you'll have access to dispatch() on props in the List component and can proceed just as in question 1.
Does the List component have an ancestor with access to the dispatch() function. If so, you can create an action dispatching function at that level and pass it down as a prop until you reach the List component, where you can call that function.
As I said, your particular use case will determine the suitability of each approach. If you have any more questions, I'm happy to help, so let me know.
Can use something like this.
import { Component } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import * as TodoActionCreators from './TodoActionCreators'
console.log(TodoActionCreators)
// {
// addTodo: Function,
// removeTodo: Function
// }
class TodoListContainer extends Component {
componentDidMount() {
// Injected by react-redux:
let { dispatch } = this.props
// Note: this won't work:
// TodoActionCreators.addTodo('Use Redux')
// You're just calling a function that creates an action.
// You must dispatch the action, too!
// This will work:
let action = TodoActionCreators.addTodo('Use Redux')
dispatch(action)
}
render() {
// Injected by react-redux:
let { todos, dispatch } = this.props
// Here's a good use case for bindActionCreators:
// You want a child component to be completely unaware of Redux.
let boundActionCreators = bindActionCreators(TodoActionCreators, dispatch)
console.log(boundActionCreators)
// {
// addTodo: Function,
// removeTodo: Function
// }
return (
<TodoList todos={todos}
{...boundActionCreators} />
)
// An alternative to bindActionCreators is to pass
// just the dispatch function down, but then your child component
// needs to import action creators and know about them.
// return <TodoList todos={todos} dispatch={dispatch} />
}
}
export default connect(
state => ({ todos: state.todos })
)(TodoListContainer)
One most common way to dispatch an action in component is to dispose the action to props using mapDispatchToProps. Then you can dispatch an action from props.
componentWillMount() {
this.props.actions.loadPropImages();
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(imageActions, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(EditorPage);

What is mapDispatchToProps?

I was reading the documentation for the Redux library and it has this example:
In addition to reading the state, container components can dispatch actions. In a similar fashion, you can define a function called mapDispatchToProps() that receives the dispatch() method and returns callback props that you want to inject into the presentational component.
This actually makes no sense. Why do you need mapDispatchToProps when you already have mapStateToProps?
They also provide this handy code sample:
const mapDispatchToProps = (dispatch) => {
return {
onTodoClick: (id) => {
dispatch(toggleTodo(id))
}
}
}
What is this function and why it is useful?
I feel like none of the answers have crystallized why mapDispatchToProps is useful.
This can really only be answered in the context of the container-component pattern, which I found best understood by first reading:Container Components then Usage with React.
In a nutshell, your components are supposed to be concerned only with displaying stuff. The only place they are supposed to get information from is their props.
Separated from "displaying stuff" (components) is:
how you get the stuff to display,
and how you handle events.
That is what containers are for.
Therefore, a "well designed" component in the pattern look like this:
class FancyAlerter extends Component {
sendAlert = () => {
this.props.sendTheAlert()
}
render() {
<div>
<h1>Today's Fancy Alert is {this.props.fancyInfo}</h1>
<Button onClick={sendAlert}/>
</div>
}
}
See how this component gets the info it displays from props (which came from the redux store via mapStateToProps) and it also gets its action function from its props: sendTheAlert().
That's where mapDispatchToProps comes in: in the corresponding container
// FancyButtonContainer.js
function mapDispatchToProps(dispatch) {
return({
sendTheAlert: () => {dispatch(ALERT_ACTION)}
})
}
function mapStateToProps(state) {
return({fancyInfo: "Fancy this:" + state.currentFunnyString})
}
export const FancyButtonContainer = connect(
mapStateToProps, mapDispatchToProps)(
FancyAlerter
)
I wonder if you can see, now that it's the container 1 that knows about redux and dispatch and store and state and ... stuff.
The component in the pattern, FancyAlerter, which does the rendering doesn't need to know about any of that stuff: it gets its method to call at onClick of the button, via its props.
And ... mapDispatchToProps was the useful means that redux provides to let the container easily pass that function into the wrapped component on its props.
All this looks very like the todo example in docs, and another answer here, but I have tried to cast it in the light of the pattern to emphasize why.
(Note: you can't use mapStateToProps for the same purpose as mapDispatchToProps for the basic reason that you don't have access to dispatch inside mapStateToProp. So you couldn't use mapStateToProps to give the wrapped component a method that uses dispatch.
I don't know why they chose to break it into two mapping functions - it might have been tidier to have mapToProps(state, dispatch, props) IE one function to do both!
1 Note that I deliberately explicitly named the container FancyButtonContainer, to highlight that it is a "thing" - the identity (and hence existence!) of the container as "a thing" is sometimes lost in the shorthand
export default connect(...)
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
syntax that is shown in most examples
It's basically a shorthand. So instead of having to write:
this.props.dispatch(toggleTodo(id));
You would use mapDispatchToProps as shown in your example code, and then elsewhere write:
this.props.onTodoClick(id);
or more likely in this case, you'd have that as the event handler:
<MyComponent onClick={this.props.onTodoClick} />
There's a helpful video by Dan Abramov on this here:
Redux: Generating Containers with connect() from React Redux (VisibleTodoList)
mapStateToProps() is a utility which helps your component get updated state(which is updated by some other components),
mapDispatchToProps() is a utility which will help your component to fire an action event (dispatching action which may cause change of application state)
mapStateToProps, mapDispatchToProps and connect from react-redux library provides a convenient way to access your state and dispatch function of your store. So basically connect is a higher order component, you can also think as a wrapper if this make sense for you. So every time your state is changed mapStateToProps will be called with your new state and subsequently as you props update component will run render function to render your component in browser. mapDispatchToProps also stores key-values on the props of your component, usually they take a form of a function. In such way you can trigger state change from your component onClick, onChange events.
From docs:
const TodoListComponent = ({ todos, onTodoClick }) => (
<ul>
{todos.map(todo =>
<Todo
key={todo.id}
{...todo}
onClick={() => onTodoClick(todo.id)}
/>
)}
</ul>
)
const mapStateToProps = (state) => {
return {
todos: getVisibleTodos(state.todos, state.visibilityFilter)
}
}
const mapDispatchToProps = (dispatch) => {
return {
onTodoClick: (id) => {
dispatch(toggleTodo(id))
}
}
}
function toggleTodo(index) {
return { type: TOGGLE_TODO, index }
}
const TodoList = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)
Also make sure that you are familiar with React stateless functions and Higher-Order Components
Now suppose there is an action for redux as:
export function addTodo(text) {
return {
type: ADD_TODO,
text
}
}
When you do import it,
import {addTodo} from './actions';
class Greeting extends React.Component {
handleOnClick = () => {
this.props.onTodoClick(); // This prop acts as key to callback prop for mapDispatchToProps
}
render() {
return <button onClick={this.handleOnClick}>Hello Redux</button>;
}
}
const mapDispatchToProps = dispatch => {
return {
onTodoClick: () => { // handles onTodoClick prop's call here
dispatch(addTodo())
}
}
}
export default connect(
null,
mapDispatchToProps
)(Greeting);
As function name says mapDispatchToProps(), map dispatch action to props(our component's props)
So prop onTodoClick is a key to mapDispatchToProps function which delegates furthere to dispatch action addTodo.
Also if you want to trim the code and bypass manual implementation, then you can do this,
import {addTodo} from './actions';
class Greeting extends React.Component {
handleOnClick = () => {
this.props.addTodo();
}
render() {
return <button onClick={this.handleOnClick}>Hello Redux</button>;
}
}
export default connect(
null,
{addTodo}
)(Greeting);
Which exactly means
const mapDispatchToProps = dispatch => {
return {
addTodo: () => {
dispatch(addTodo())
}
}
}
mapStateToProps receives the state and props and allows you to extract props from the state to pass to the component.
mapDispatchToProps receives dispatch and props and is meant for you to bind action creators to dispatch so when you execute the resulting function the action gets dispatched.
I find this only saves you from having to do dispatch(actionCreator()) within your component thus making it a bit easier to read.
React redux: connect: Arguments

Resources