I am trying to dispatch a redux action when an element is clicked. Here is how I currently have things set up
the action
export function removeItem(idx) {
// remove the item at idx
return {
type: "DESTROY_ITEM",
payload: {idx: idx}
}
}
container component
import ItemUi from '../ui/Item';
import { connect } from 'react-redux'
import { removeItem } from '../../actions'
const mapDispatchToProps = dispatch => {
return({
destroyItem: (index) => {
dispatch(
removeItem(index)
)
}
})
}
export default connect(null, mapDispatchToProps)(ItemUi)
ui component
import React, { Component, PropTypes } from 'react';
class Item extends Component {
// ... methods removed for length
render() {
return (
<div>
<span
className="btn btn-primary btn-xs glyphicon glyphicon-trash"
onClick={() => {this.props.destroyItem(this.props.index)}}></span>
</div>
)
}
}
DestroyItem.propTypes = {
onRemoveItem: PropTypes.func
}
export default Item
the top level component
import React, { Component } from 'react';
import Item from './Item'
class App extends Component {
render() {
return(
<div className="container">
<NewItem/>
<ClearAll/>
<div className="panel-group" id="accordion">
{this.props.item.map(this.eachItem)} // <-- renders each item
</div>
</div>
)
}
}
export default App;
When the user clicks the span element shown in the Item ui component I want to fire the destroyItem action passing in the component's index prop. Instead I get the error
TypeError: _this2.props.destroyItem is not a function
Can you please provide some guidance on how I should do this? Let me know if there is any other useful context I can provide.
Try:
const mapDispatchToProps = {
destroyItem: removeItem
}
Related
This is a react beginners exercise so I'm looking for the simplest solution. I need to convert these 3 class components into functional components. I'm currently learning React so any helpful comments would also be appreciated. Thanks in advance!
APP.JS
import React from 'react'
import List from './components/List'
class DataArr extends React.Component {
render() {
const fruits = ["banana", "apple", "pear", "melon", "lemon"]
return (
<div className="DataArr">
<List fruits={fruits}/>
</div>
)
}
}
export default DataArr;
LIST.JS
import React from "react";
import Item from "./Item";
class List extends React.Component {
render() {
return (
<div>
{this.props.fruits.map((fruit, index) => {
return <Item key={index} fruit={fruit} />;
})}
</div>
);
}
}
export default List;
ITEM.JS
import React from 'react'
class Item extends React.Component{
render() {
return (
<div>
{this.props.fruit}
</div>
)
}
}
export default Item;
This is a step by step answer on How to Convert React Class Component to Functional Component which is nicer, cleaner and easier to read:
You need to change the class to a function
Remove render function
Remove this keyword
If you have state in your Class Component use hooks and in particular useState or useReducer hook
If you used lifecycle methods in your Class Component you can almost use useEffect hook in every situation. (just need to be comfortable with it which you can read more about it here and here)
App.js would be:
import React from 'react'
import List from './components/List'
// class DataArr extends React.Component { //<-- Remove this line
const DataArr = () => { // <-- Create a function Component
// render() { // <-- remove render function because you don't need it
const fruits = ["banana", "apple", "pear", "melon", "lemon"]
return (
<div className="DataArr">
<List fruits={fruits}/>
</div>
)
// } // this curly brace is for render function
}
export default DataArr;
List.js would be:
import React from "react";
import Item from "./Item";
// class List extends React.Component { //<-- Remove this line
const List = (props) => {
// render() { // <-- remove render function because you don't need it
return (
<div>
{
// this.props.fruits.map((fruit, index) => { <-- Change this.props to props
props.fruits.map((fruit, index) => {
return <Item key={index} fruit={fruit} />;
})}
</div>
);
// } // this curly brace is for render function
}
export default List;
and the ITEM.js would be like this:
import React from 'react'
// class Item extends React.Component{ //<-- Remove this line
const Item = (props) => { // <-- Create a function Component
// render() { // <-- remove render function because you don't need it
return (
<div>
{
// this.props.fruit // <-- change this.props to props
props.fruit
}
</div>
)
}
// } // this curly brace is for render function
export default Item;
In this particular instance, conversion is straight forward as they are simple 'dumb' components. You just remove the classes, convert them into standard functions with their props passed as a parameter, remove the render() and replace with a return.
APP.JS
import React from 'react'
import List from './components/List'
function DataArr() {
const fruits = ["banana", "apple", "pear", "melon", "lemon"];
return (
<div className="DataArr">
<List fruits={fruits}/>
</div>
);
}
export default DataArr;
LIST.JS
import React from "react";
import Item from "./Item";
function List({ fruits }) {
return (
<div>
{fruits.map((fruit, index) => {
return <Item key={index} fruit={fruit} />;
})}
</div>
);
}
export default List;
ITEM.JS
import React from 'react';
function Item({ fruit }) {
return (
<div>
{fruit}
</div>
);
}
export default Item;
APP.JS
import React from 'react';
import List from './components/List';
const DataArr = () => {
const fruits = ["banana", "apple", "pear", "melon", "lemon"];
return (
<div className="DataArr">
<List fruits={fruits} />
</div>
)
}
export default DataArr;
LIST.JS
import React from 'react';
import Item from './Item';
const List = (props) =>
{props.fruits.map(fruit, index) =>
<Item key={index} fruit={fruit} />};
export default List;
ITEM.JS
import React from 'react';
const Item = (props) => {props.fruit};
export default Item;
I have a several react components in react-redux application. Most of them are working find, but the below "TagItemWidget" does not appear to bind the state or dispatchers to props. I have confirmed that the dispatch function works and fires the reducer. The same function and state can be bound on other components. I have done a trace and observed that the bind function is firing. However, in both "console.log" outputs, props is empty. componentDidMount and render appear to be called only once, when the page loads - never again. What gives?
UPDATE: If I move my <TagItemWidget /> into the spot where <TagItemButton /> is, it populates the dispatchers. Is there a problem with my TagItemButton?
TagItemWidget:
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { tags_list } from "../../actions/tags";
export class TagItemWidget extends React.Component {
static propTypes = {
cases: PropTypes.array.isRequired,
activeCase: PropTypes.string
};
constructor(props) {
super(props);
}
componentDidMount() {
console.log(this)
}
render() {
console.log(this)
return (
<Fragment>
<div key={Math.random} >
{this.props.activeCase}
</div>
</Fragment>
)
}
}
const mapStateToProps = (state) => ({
cases: state.tags.tags,
activeCase: state.cases.activeCase
});
export default connect(mapStateToProps, { tags_list })(TagItemWidget);
The including component, TagItemButton:
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { TagItemWidget } from './TagItemWidget';
export class TagItemButton extends Component {
render() {
return (
<Fragment>
<a href="#" className="list-group-item list-group-item-action" id="controls_tagitem"
data-toggle="modal" data-target="#tagItemModal">
Tag Item
</a>
<div className="modal fade" id="tagItemModal" tabIndex="-1"
role="dialog" aria-labelledby="tagItemModalLabel"
aria-hidden="true">
<div className="modal-dialog" role="document">
<div className="modal-content">
<div className="modal-header">
<h5 className="modal-title"
id="tagItemModalLabel">
Tag Item
</h5>
<button type="button" className="close"
data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div className="modal-body">
<TagItemWidget />
</div>
</div>
</div>
</div>
</Fragment>
)
}
}
export default TagItemButton;
actions/tags.js
import { TAGS_LIST } from "./types";
import { createMessage, returnErrors } from "./messages";
export const tags_list = ( case_id ) => dispatch => {
if ( case_id != null ) {
console.log("dispatging TAGS_LIST")
axios
.get("/OMNI_api/api/tag/listbycase/?case_id="+case_id)
.then(response => {
dispatch
({
type: TAGS_LIST,
payload: response.data
})
})
}
}
If you are using redux-thunk. Which I think you are.
dispatch needs to be spread from the thunk like so
export const tags_list = ( case_id ) => ({ dispatch }) => {
You need to bindActionCreators
either before passing them to the connect function
const mapDispatch = (dispatch) => bindActionCreators({ tag_list }, dispatch);
export default connect(mapStateToProps, mapDispatch)(TagItemWidget);
inside the constructor
https://redux.js.org/api/bindactioncreators
I've read the articles on here on how to do this and have chosen the withRouter(({ history }) => history.push("/")); method, but my code below isn't working.. What am I doing wrong?
import React from "react";
import SearchBox from "./SearchBox";
import { withRouter } from "react-router-dom";
class SearchParams extends React.Component {
handleSearchSubmit() {
withRouter(({ history }) => history.push("/"));
}
render() {
return (
<div className="search-route">
<SearchBox search={this.handleSearchSubmit} />
</div>
);
}
}
export default SearchParams;
withRouter is a higher order component which takes a component as first argument and will make it so that component gets the history added to its regular props.
You can instead use it on the component when you export it, and access the history from this.props.history.
class SearchParams extends React.Component {
handleSearchSubmit = () => {
this.props.history.push("/");
};
render() {
return (
<div className="search-route">
<SearchBox search={this.handleSearchSubmit} />
</div>
);
}
}
export default withRouter(SearchParams);
I am building a component which is a gallery that consists of a main gallery image and a list of thumbnails under the main image. This component will be used across multiple pages so each page will have it own thumbnails/images. I have worked out how to get the correct images into the gallery depending on the page using redux and a store. However I cant figure out the functionality in getting the main image to change when the corresponding thumbnail is clicked, any ideas or suggestion how I could approach this?
import React, { Component } from 'react';
import { connect } from 'react-redux';
class Gallery extends Component {
render() {
let img = this.props.bouq.map((el, index) => {
return(
<img src={"/images/" + el + ".jpg"} alt="." key={index}/>
);
})
return(
<section className="gallery">
<div className="mainImage">
<img src='/images/bouquets.jpg' alt="."/>
</div>
<div className="thumbnails">
{img}
</div>
</section>
);
}
}
const mapStateToProps = state => {
return {
bouq: state.bouquets
};
}
export default connect(mapStateToProps)(Gallery);
You can leverage the access to every individual element inside the map, onClick on an element, you can update you mainImage in your redux store.
You'll also need to create an action that updates the mainImage (depending on how you manage your redux actions)
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { updateMainImage } from './actions'
class Gallery extends Component {
updateMainImage = tumbnail => () => {
this.props.dispatch(updateMainImage(thumbnail))
}
render() {
const { mainImg, bouq } = this.props
return (
<section className="gallery">
<div className="mainImage">
<img src={`/images/${mainImage}.jpg`} alt={mainImage} />
</div>
<div className="thumbnails">
{bouq.map(thum => (
<img
key={thumb}
src={`/images/${thumb}.jpg`}
alt={thumb}
onClick={this.updateMainImage(thumb)}
/>
))}
</div>
</section>
)
}
}
const mapStateToProps = state => {
return {
bouq: state.bouquets,
mainImage: state.mainImage,
}
}
export default connect(mapStateToProps)(Gallery)
i don't know what im doing wrong.
I thought i got the point how to implement with redux.
My Problem is that the state of the Component is not propagating to the Component after the state changed. I know we have to create new state object and i'm sure im doing it right. But there is no changes.
And the other question is, why the state is in the object "resetLink" see image. Why is it not stored in the state Object?
Please help me to get it work.
Action for the redux:
export const sendResetLink = (email) => {
return {
type: RESET_LINK,
email
}
}
class App extends React.Component {
render() {
return <VisibleResetLink/>
}
}
This is where the magic with props and dispatch function happens:
import {connect} from 'react-redux';
import SendResetLink from '../components/SendResetLink.jsx';
import {sendResetLink} from '../actions/index'
import _ from 'lodash';
const mapDispatchToProps = (dispatch) => {
return {
onClick: (email) => {
dispatch(sendResetLink(email))
}
}
}
const mapStateToProps = (state) => {
console.log("actual state", state);
return _.assign({} , {state: state.resetLink});
}
const VisibleResetLink = connect(
mapStateToProps,
mapDispatchToProps
)(SendResetLink)
export default VisibleResetLink
This is the Component which load props but never rerender them when they change.
import React, { PropTypes } from 'react';
import ReactDOM from 'react-dom';
class SendResetLink extends React.Component {
constructor(props, email, result) {
super(props);
console.log('props',props);
this.onClick = props.onClick;
this.result = props.state.result;
this.input = props.state.email;
}
renderResult (result){
console.log("renderResult",result);
if(result)
return <div className="card-panel deep-orange lighten-4">Invalid username and password.</div>
}
render() {
return <div>
{this.renderResult(this.result)}
{this.result}
<form className="col s12" action="/login" method="post" onSubmit={e => {
e.preventDefault()
if (!this.input.value.trim()) {
return
}
this.onClick(this.input.value);
this.input.value = ''
} }>
<div className="row">
<div className="input-field col s12">
<i className="material-icons prefix">email</i>
<input id="email" type="email" name="email" className="validate" ref = {(node) => this.input = node } />
<label for="email">Email</label>
</div>
</div>
<div className="divider"></div>
<div className="row">
<div className="col m12">
<p className="right-align">
<button className="btn btn-large waves-effect waves-light" type="submit" name="action">Send reset key</button>
</p>
</div>
</div>
</form></div>
}
}
SendResetLink.propTypes = {
onClick: PropTypes.func.isRequired,
state: PropTypes.object.isRequired
}
export default SendResetLink
And this is the other relevant code snippet, where the reducer is implemented.
import _ from 'lodash';
const resetLink = (state = {email:"test#test.de", result:true}, action) => {
console.log('state', state)
switch (action.type) {
case 'RESET_LINK':
return _.assign({},state,{email: action.email, result: false});
default:
return state
}
}
export default resetLink;
import { combineReducers } from 'redux'
import resetLink from './ResetLinkReducer.jsx'
const resetApp = combineReducers({
resetLink
})
export default resetApp
import App from './components/app.jsx';
import {Provider} from 'react-redux';
import { createStore } from 'redux';
import { render } from 'react-dom';
import React from 'react';
import resetApp from './reducers/index.js';
let store = createStore(resetApp,{});
console.log(store)
render(<Provider store={store}>
<App />
</Provider>, document.getElementById('sendResetLinkComponent'));
console logs. After click the renderResult should change to false. But it stays on true
You set this.result in your SendResetLink component only once in the constructor, which is only executed when the component is instantiated. The constructor will not be executed every time the application's state changes:
this.result = props.state.result;
Instead of assigning this part of the state to an instance attribute of your react component, simply use the props attribute directly in your render() function:
{this.renderResult(this.props.state.result)}