How to dispatch an action when a button is clicked? - reactjs

//action code
export const CLEAR_COMPLETED = 'CLEAR_COMPLETED'
export const clearCompleted = () => {
return{
type: CLEAR_COMPLETED
}
}
//reducer code
case CLEAR_COMPLETED:
return state.map(todo => {if (todo.completed)
{return {...todo, show:false}}
else {return todo}})
Problem dispatching action on Todo application in react-redux.
import React from 'react'
import { connect } from 'react-redux'
import { clearCompleted } from '../actions'
const ClearButton = ({dispatch}) => {
return(
<button fluid onClick={e => {dispatch(clearCompleted())}}>
Clear Completed
</button>
)
}
export default ClearButton
Trying to change the store by clicking on Clear Completed Button. Clear Completed Button should remove the completed todos from the store and todo list should be updated. I am trying to call 'clearCompleted' action with Clear Completed Button.

The difficulty you're having here is that your component doesn't know anything about the Redux store, and the dispatch function will not be in its props. The most basic way you can make dispatch available would be this:
export default connect()(ClearButton)
This will allow you to use dispatch(clearCompleted()) without messing around further with mapDispatchToProps. You'd have to change its definition so it's not a stateless component though.
However, you should probably ask yourself whether a tiny button really needs connect at all? You could probably just pass the correct function down from the containing component:
// TodoList.js
class TodoList extends Component {
render () {
return (
...
<ClearButton clearCompleted={this.props.clearCompleted} />
)
}
}
const mapStateToProps = state => ({
// ...
})
const mapDispatchToProps = dispatch => ({
clearCompleted: () => dispatch(clearCompleted())
})
export default connect(mapStateToProps, mapDispatchToProps)(TodoList)
Then the function will be in ClearButton's props without it needing to be connected:
<button onClick={this.props.clearCompleted}>

You can do it by wrapping your component in connect.
connect accepts two arguments as first call, mapStateToProps for mapping your store properties into your component's props and mapDispatchToProps for mapping action creators into your component's props. It's also followed by another call to that function with the Component name of yours written in class syntax.
If you insist in using stateless components with connect, you can use compose utility from redux.
import React from 'react'
import {bindActionCreators} from 'redux';
import { connect } from 'react-redux'
import { clearCompleted } from '../actions'
class ClearButton extends React.Component {
render() {
const {clearCompleted} = this.props;
return(
<button fluid onClick={clearCompleted}>
Clear Completed
</button>
)
}
}
const mapDispatchToProps = dispatch => bindActionCreators({ clearCompleted }, dispatch);
export default connect(null, mapDispatchToProps)(ClearButton);

Related

How to get redux store or redux dispatch in CLASS react component using typescript

I know how to to dispatch and get store using functional component. There is a great documentation about it here
But what about class component? How to get store and fire dispatch using class component. There is nothing said about it.
Lets say I defined hooks.ts:
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
import { RootState, AppDispatch } from './store/store';
export const useAppDispatch = () => useDispatch<AppDispatch>()
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
And I need this functional component make as class:
import {useAppDispatch, useAppSelector} from "../../../hooks"
import { setRelease } from "./../store/factorySlice/factorySlice"
const X: React.FC = () => {
const {selectedRelease} = useAppSelector(store => store.factory)
const dispatch = useAppDispatch()
return (
<>
{selectedRelease}
<button onClick={() => dispatch(setRelease())}>Click</button>
</>
)
}
Redux Toolkit is our official recommended approach for writing Redux logic. It wraps around the Redux core, and contains packages and functions that we think are essential for building a Redux app
You can use it with class component by using react-redux's connect API:
import { connect } from "react-redux";
import { setRelease } from "./../store/factorySlice/factorySlice"
class X extends React.Component {
render() {
return (
<>
{this.props.selectedRelease}
<button onClick={() => this.props.dispatch(setRelease())}>Click</button>
</>
);
}
}
const mapStateToProps = (state) => ({
selectedRelease: state.factory.selectedRelease
});
export default connect(mapStateToProps)(X);
This was the first implementation. You would connect the class component to the store like
connect()(MyComponent)
Where Class is MyComponent

Handling button state and actions in redux

I am new to react and redux.
Overview of app: I have a ToggleButtonGroup with two buttons. When a button is click I want to output a table for the respective button. The tables we filtered based on the button click.
Question: I am not sure how to setup an action, state and reducer in my project for the button functionality. My button is a component. Is it best practice to use actions and reducers for buttons? How would I pass these actions to other components? Any examples or resources is appreciated.
This is my Button.tsx file
import React, { Component, useState } from 'react'
import ToggleButtonGroup from 'react-bootstrap/ToggleButtonGroup';
import ToggleButton from 'react-bootstrap/ToggleButton';
function ToggleButtonGroupControlled() {
const [value, setValue] = useState([1, 2]);
return (
<ToggleButtonGroup type="checkbox" value={value} onChange={() => setValue(value)}>
<ToggleButton value={1}>PA Probes</ToggleButton>
<ToggleButton value={2}>Convential Probes</ToggleButton>
</ToggleButtonGroup>
);
}
export class Buttons extends Component {
render() {
return (
<div>
<ToggleButtonGroupControlled />
</div>
)
}
}
export default Buttons
This is my types.ts file
export const TOGGLE_PA_PROBES = 'TOGGLE_PA_PROBES';
export const TOGGLE_CONVENTIONAL_PROBES = 'TOGGLE_CONVENTIONAL_PROBES';
This is buttonAction.ts file
import{ TOGGLE_PA_PROBES, TOGGLE_CONVENTIONAL_PROBES } from './types';
export function togglePAProebs(){
}
export function toggleConventionalProbes(){
}
This is my buttonReducers.ts file
import{ TOGGLE_PA_PROBES, TOGGLE_CONVENTIONAL_PROBES } from '../Actions/types';
export function ButtonReducer(state, action){
switch(action.type){
default:
return state;
}
}
I will assume you know the basics of redux and react-redux. In short, you know that an action is dispatched to a store.
In a real world situation, it is as if you change a channel on your TV, (1) you dispatch a CHANGE_CHANNEL action to your remote control, (2) the remote control will receive the action dispatched and send that action with the channel information to your TV (reducer), then (3) your TV (reducer) will talk to your cable provider (store), and return the channel data back if it is available to you.
That flow is important because it can be used in your example as well. To begin with, imagine you have a store with some probes. On toggling your checkbox, you will dispatch an action FILTER, which will filter out the probes that need to be shown from a database list, for example, then call your reducer with the filtered probes to update your store and send that data back to your dispatcher.
When you first load your page, I also imagine you want to show the list of all your probes, so you would also need a FETCH action to be dispatched upon componentDidMount. Having that in mind, we can come up with a types file like this:
types.js
export const TOGGLE_PROBES = "TOGGLE_PROBES";
export const LOAD_PROBES = "LOAD_PROBES";
Now that the types are defined, we can create our actions,
import { TOGGLE_PROBES, LOAD_PROBES } from "./types";
const dbProbes = [
{ title: "pa probe 1", type: 1 },
{ title: "pa probe 2", type: 1 },
{ title: "conditional probe 1", type: 2 },
{ title: "conditional probe 1", type: 2 }
];
function toggleProbes(filtered) {
return {
type: TOGGLE_PROBES,
filtered: filtered
};
}
function loadProbes(probes) {
return {
type: LOAD_PROBES,
probes: probes
};
}
export function fetchProbes() {
return function(dispatch) {
dispatch(loadProbes(dbProbes));
};
}
export function filterProbes(filter) {
return function(dispatch) {
const filtered = dbProbes.filter(probe => filter.includes(probe.type));
dispatch(toggleProbes(filtered));
};
}
Notice that I created a fake list of probes called dbProbes. In a real world situation, you would probably be reaching out to a database to filter your probes.
After having all of the actions set up, you can finally work on the reducer and update your state accordingly,
import { TOGGLE_PROBES, LOAD_PROBES } from "./types";
const initialState = {
probes: []
};
function ButtonReducer(state = initialState, action) {
switch (action.type) {
case TOGGLE_PROBES:
return { ...state, probes: action.filtered };
case LOAD_PROBES:
return { ...state, probes: action.probes };
default:
return state;
}
}
export default ButtonReducer;
In order to be able to dispatch this in your components, you do need to set redux up. In your index.js, you could accomplish that by using both react, react-redux and react-thunk. React-thunk is a middleware which is often used with redux for async calls.
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import "./styles.css";
import Buttons from "./Buttons";
import rootReducer from "./store/reducer";
const store = createStore(rootReducer, applyMiddleware(thunk));
const rootElement = document.getElementById("root");
ReactDOM.render(
<Provider store={store}>
<Buttons />
</Provider>,
rootElement
);
First, in your Button components, maybe you just want to show the initial list first, so to do that you need to use a react-redux high order component called connect, which will, as the name states, connect your store and map your store's state to dispatchers and props.
import React, { Component } from "react";
import ToggleButtonGroupControlled from "./ToggleButtonGroupControlled";
import { connect } from "react-redux";
import { fetchProbes } from "./store/action";
export class Buttons extends Component {
componentDidMount() {
this.props.fetchProbes();
}
render() {
let probes = null;
if (this.props.probes) {
probes = this.props.probes.map(probe => <li>{probe.title}</li>);
}
return (
<div>
<ToggleButtonGroupControlled />
<ul>{probes}</ul>
</div>
);
}
}
function mapStateToProps(state) {
return {
probes: state.probes
};
}
function mapDispatchToProps(dispatch) {
return {
fetchProbes: () => dispatch(fetchProbes())
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Buttons);
Finally, your toggle button component can properly dispatch a TOGGLE_FILTER action to your actions.js, which will filter the probes, then send the action to reducer, which will update your state and return the updated state, which at this point is mapped to your component props in your Button.js component.
import React, { useState } from "react";
import { ToggleButtonGroup } from "react-bootstrap";
import { ToggleButton } from "react-bootstrap";
import { connect } from "react-redux";
import { filterProbes } from "./store/action";
function ToggleButtonGroupControlled(props) {
const [value, setValue] = useState([1, 2]);
const toggleChangeHandler = newValue => {
setValue(newValue);
props.filterProbes(newValue);
};
return (
<ToggleButtonGroup
type="checkbox"
value={value}
onChange={e => toggleChangeHandler(e)}
>
<ToggleButton value={1}>PA Probes</ToggleButton>
<ToggleButton value={2}>Convential Probes</ToggleButton>
</ToggleButtonGroup>
);
}
function mapDispatchToProps(dispatch) {
return {
filterProbes: types => dispatch(filterProbes(types))
};
}
export default connect(
null,
mapDispatchToProps
)(ToggleButtonGroupControlled);
The cool thing about redux is that it allows for a global state, so you dispatch and change your list of probes from the ToggleButtonGroupControlled component, but that store (list of probes) it's also available for other components that you decide to connect to your store, in this case, Button.js.
You can check the final code running and ready for your problem here:
To understand more about redux-thunk, go here: https://github.com/reduxjs/redux-thunk
Also, there's a free redux book here that you could maybe take a quick look at: https://leanpub.com/redux-book
Redux might be overkill for this example, it all depends however at what level in the component hierarchy your table lives in relation to your ToggleButtonGroupControlled component.
It would help in your question to see the table component, is it in a separate file / route?
I would start simple and pass the value up out of your ToggleButtonGroupControlled component by creating an onChange prop. Right now your ToggleButtonGroupControlled component doesn't add much more functionality
than the third party ToggleButtonGroup component so it's a little hard to reason about.
Also if you do choose to go the redux route because you need this data in the global state then you can probably forego the local state you have here and select it from your redux store and pass into the component

Calling action creator inside the function. Error:Actions may not have an undefined "type" property?

I know action creator should have a type of property only then it would be able to dispatch. Since I am having a function call which ultimately leads to one action creator which have type property then Why it is showing me this problem.
When I tried to directly dispatch start game action creator it works but since I have to implement some more function inside them so I needed then inside the function.
How to implement the same?
Menu.js
import React, { Component } from 'react';
import {connect} from 'react-redux';
import {startGame} from '../actions';
import {loadMenu} from '../actions';
import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux';
const page_Banner={
marginTop:'35px',
fontSize:'45px',
textAlign:'center',
letterSpacing:'20px',
fontWeight:'bold'
};
const spacebar_screen={
marginTop:'35px',
color:'grey'
}
class Menu extends Component {
componentDidMount() {
this.props.dispatch(loadMenu());
console.log(this.props.dispatch);
console.log(this.props.isPlaying);
}
render() {
return (
<div style={page_Banner}>
Redux Tetris
{!this.props.isPlaying?<h2 style={spacebar_screen}>Press spacebar to start the game</h2>:null}
</div>
)
}
}
Menu.propTypes={
isPlaying:PropTypes.bool,
}
// function mapDispatchToProps(dispatch){
// return bindActionCreators({loading:loadMenu},dispatch);
// }
const mapStateToProps = (state) => ({
isPlaying: state.gameStatus.currentState !== 'IDLE',
});
export default connect(mapStateToProps)(Menu);
Action.js
import constants from "../gameConstants/constants";
export const startGame=()=>{
const ShapeMapping=constants;
const current_Shapeno=Math.floor(Math.random()*7);
const next_Shapeno=Math.floor(Math.random()*7);
const current_Shape=ShapeMapping[current_Shapeno];
const next_Shape=ShapeMapping[next_Shapeno];
return {
type:"START_GAME",
current_Shape,
next_Shape
};
}
export const pauseGame = () => ({
type: "PAUSE_GAME",
});
export const unpauseGame = () => ({
type: "UNPAUSE_GAME",
});
export const gameOver = () => ({
type: "GAME_OVER",
});
export const loadMenu=()=>({
function(dispatch,getState){
function handleSpacebar(event){
if(event.keyCode==32){
dispatch(loadGame());
window.removeEventListener('keyup',handleSpacebar);
console.log('here')
}
}
window.addEventListener('keyup',handleSpacebar);
}
})
export const loadGame=()=>({
function (dispatch,getState){
dispatch(startGame());
}
})
The issue is in loadMenu and loadGame action creators. You're returning an object with an anonymous function which doesn't make any sense. An action creator is supposed to return an object with a type and the minimal data to define the action and return a function if you're using redux-thunk.
Keep the actions creators clean like you've done in gameOver and handle everything else in reducers or using the redux pub/sub pattern.
See this answer by Dan Abramov https://github.com/reduxjs/redux/issues/787

React, Redux - pass function from component A to other components

import React from "react";
import OtherComponent from "./OtherComponent";
class Main extends React.Component {
constructor(props) {
super(props);
this.runMyFunction = this.runMyFunction.bind(this);
this.myFunction = this.myFunction.bind(this);
}
runMyFunction(event) {
event.preventDefault();
this.myFunction();
}
myFunction() {
return console.log("I was executed in Main.js");
}
render() {
return (
<div>
<OtherComponent runMyFunction={this.runMyFunction} />
</div>
);
}
}
export default Main;
import React from "react";
class OtherComponent extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.props.runMyFunction();
}
render() {
return (
<div>
<button onClick={this.handleClick} />Click me to execute function from Main </button>
</div>
);
}
}
export default OtherComponent;
I'm new in redux and don't know how to pass and run that function in other component. It was easy not using redux, just pass as props like in example above.
I have folder with actions, components, containers and reducers.
Now I have Main.js where I have
import React from "react";
const Main = ({data, getData}) => {
const myFunction = () => {
return "ok";
};
return (
<div>
<p>This is main component</p>
</div>
);
};
export default Main;
In MainContainer.js I got:
import Main from "../../components/Main/Main";
import { connect } from "react-redux";
import {
getData
} from "../../actions";
function mapStateToProps(state) {
return {
data: state.main.data
};
}
const mapDispatchToProps = (dispatch) => {
return {
getData: () => dispatch(getData())
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(Main);
So how I can run function myFunction() in OtherComponent.js:
import React from "react";
const OtherComponent = ({executeFunctionInMainComponent}) => {
return (
<div>
<button onClick={executeFunctionInMainComponent}>run action</button>
</div>
);
};
export default OtherComponent;
I need to just run, not pass whole function, just execute myFunction in Main.js but action to run this function will came from OtherComponent.
So first i have to mention that i believe that you have a misconception of redux. This isn't to allow for functions created in components to be reused in different locations. This is to move that logic to a reducer outside of your function which would allow it to be used wherever you wired it with {connect} from react-redux. So you will need a couple of files (for clarity). First you're going to need an action file which we'll name myReturnOkAction.
export const myReturnOkAction = (/*optional payload*/) => {
return {
type: 'PRINT_OK',
}
}
Redux Actions
This is what you're going to call in your mapDispatchToProps function where you're going to trigger this event. You're going to have to import it into your OtherComponent so import {myReturnOkAction} from "/*wherever the files exists*/" and to include it in your mapDispatchToProps as okFunction: () => dispatch(myReturnOkAction())
Once you have your action your connect Higher Order Component (HOC) wrapping your main component is going to need a Reducer to modify your current store state as well as do any actions.
export const myReturnOkReducer = (state, action) => {
if(action.type === 'PRINT_OK'){
/*This is where you update your global state*/
/*i.e. return {...store, valueWanted: ok}*/
}else{
return state
}
}
Redux Reducers
So the way that this is going to move is that you're function, somewhere is going to call the action. Once the action is called its going to trigger the reducer and make any changes to the store which you need. Once the reducer has updated the store with new values its then going to update any components which are connected to it through the connect HOC which will cause them to re-render with new information.
Also my favorite image to describe how redux works.
I hope this helps.
I found an answer:
I still can pass as props in redux but I can't do this in this way: OtherComponent = ({executeFunctionInMainComponent}) => {}. I need to do in this way: OtherComponent = (props) => {} and then inside that component I have an access via props.executeFunctionInMainComponent

How to get dispatch function reference and action creater as a props inside any connected component

we get dispatch function ref in our connected component but when we use action creator argument in our connect function then it will not return dispatch function as previously.
Case 1. With only first param of connect function.
import React, { Component } from 'react';
import { connect } from 'react-redux';
class App extends Component {
render() {
console.log('this.props',this.props)
return (
<div className="App">
App
</div>
);
}
}
const mapStateToProps = (state) => {
return state
}
export default connect(mapStateToProps)(App);
Case 2. Use connect's second parameter also
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { setData } from './actions'
class App extends Component {
render() {
console.log('this.props',this.props)
return (
<div className="App">
App
</div>
);
}
}
const mapStateToProps = (state) => {
return state
}
export default connect(mapStateToProps,{ setData })(App);
I expect that in second case, dispatch function ref should also be listed in props, but its not happend .
Console output :
case 1. dispatch is showing in props https://prnt.sc/ne0fpb
case 2. No dispatch get in props https://prnt.sc/ne0djb
So please help me why i am not getting dispatch function in case 2?
Second connect parameter (mapDispatchToProps) defaults to dispatch => ({ dispatch }) function. Default value isn't applied when it's specified.
Since mapDispatchToProps is used to provide all needed dispatcher functions as component props, dispatch prop isn't needed.

Resources