Cannot call redux action in my component - javascript

I'm learning redux with react-native and I'm trying to use actions in "container-component" pattern.
I want to call a redux action in my component and I'm using mapDispatchToProps in order to do this.
But when the component calls the action, I get the famous red screen with
this.props.myDecrementor is not a function
and when I debug and console.log(this.props) in my component no actions or props are available.
Let me show my code :
myContainer.js
import { connect } from "react-redux";
import { incrementStepCount, decrementStepCount } from "../actions";
class ChooseHour extends Component {
constructor(props) {
super(props);
this.addStep = this.addStep.bind(this);
this.removeStep = this.removeStep.bind(this);
}
addStep() {
this.props.myIncrementor();
}
removeStep() {
this.props.myDecrementor();
}
render() {
return ( <
View style = {
{
flex: 1
}
} >
<
Button onPress = {
this.addStep
}
style = {
styles.btnAddEtape
}
small transparent iconLeft >
<
Text > Add Step < /Text> <
/Button> <
InputStep key = {
1
}
label = "MyStep" / >
<
/View>
);
}
}
function mapStateToProps(state) {
return {
order: state.order
};
}
function mapDispatchToProps(dispatch) {
return {
myDecrementor: () => dispatch(decrementStepCount()),
myIncrementor: () => dispatch(incrementStepCount())
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(ChooseHour);
myComponent.js
export default class InputStep extends Component {
decrementStepCountinComponent() {
this.props.myDecrementor();
}
render() {
return ( < Item style = {
styles.inputContainer
}
inlineLabel > < Label > {
this.props.label
} < /Label> <
Input style = {
styles.input
}
placeholder = "Step" / > < Icon name = "times"
type = "FontAwesome"
style = {
{
color: "red"
}
}
onPress = {
() => this.decrementStepCountinComponent()
}
/> < /
Item > );
}
}
Calling actions in container works, but not in the component ...
I've read this post What is mapDispatchToProps? but I really don't understand why it is not working.
Thank you in advance for your help.

You need to pass myDecrement() function to inputstep component from container as props.
In myContainer add
<InputStep myDecrementor = {this.props.myDecrementor}

Related

React passes wrong parameter along function

I'm using a for loop to create some svg paths but have some trouble passing a parameter alongside the function. Everything added with the props uses the correct i value, except selectRegion(i). This gets 2 as value, which I think is the final value of i after finishing the loop. How do I pass the correct i value?
componentDidMount() {
var regions = []
for (var i = 0; i < this.state.regionNames.length; i++) {
var region = <Region id={i} border={this.state.regionBorders[i]} color={this.state.regionColors[i]} selectRegion={() => this.selectRegion(i)}/>;
regions.push(region);
}
this.setState({regions: regions});
}
// Select region.
selectRegion(id) {
alert(id);
this.setState({selRegion: id});
}
Region component
import React, { Component } from 'react'
export default class Region extends Component {
constructor (props) {
super(props);
this.state = {
id: props.id,
color: props.color,
border: props.border,
opacity: "0.3",
is_selected: false
}
}
mouseEnter = (is_enter) => {
if(is_enter) {
this.setState({opacity: "0.5"});
alert(this.state.id);
this.props.selectRegion();
} else if (!this.state.is_selected) {
this.setState({opacity: "0.3"});
}
}
mouseClick = () => {
this.setState({is_selected: !this.state.is_selected})
}
render() {
return (
<path d={this.state.border}
fill={this.state.color}
fill-opacity={this.state.opacity}
onClick={() => this.mouseClick()}
onMouseEnter={() => this.mouseEnter(true)}
onMouseLeave={() => this.mouseEnter(false)}/>
)
}
}
#Norse was correct I've fixed it by doing the following:
// Generate map regions.
componentDidMount() {
var regions = []
for (let i = 0; i < this.state.regionNames.length; i++) {
var region = <Region id={i} border={this.state.regionBorders[i]} color={this.state.regionColors[i]} selectRegion={() => this.selectRegion(i)}/>;
regions.push(region);
}
this.setState({regions: regions});
}

Add Node in React Sort-able Tree Package

I'm starting to use React, and I want to add a react-tree component in my project. I am trying to a new node with the help of input and pass those data into the JSON array. But React Sortable tree not working.
I got the output in my console window. I have added the sample functionality here. would any please help me with this? Thanks for the response!!
I have One Parent Component named APP.js with two more child component with the names are Add.js & Drag.js
App Javascript file Sample code as below
import React, {
useState,
Component,
useEffect
} from "react";
import 'react-sortable-tree/style.css';
import TreeView from "./Drag&Drop";
// import Test from "./Testing";
import AddEdit from "./Add";
import './styles.css'
const Tree = (props) => {
const [info, setInfo] = useState('');
const data = (item) => {
let value = item.title;
setInfo(value);
}
console.log('data', info)
return ( <
div >
<
div className = "add-dt" >
<
div className = "left-side" >
<
AddEdit callback = {
data
}
/> <
/div> <
div className = "right-side" >
<
TreeView info = {
info
}
/> <
/div> <
/div> {
/* <Test /> */ }
<
/div>
);
}
export default Tree;
Two other component code are below followed with add.js & drag.js
ADD component js file
import React, {
useState
} from 'react';
import {
TextField
} from '#fluentui/react/lib/TextField';
import {
DefaultButton,
PrimaryButton,
Stack,
IStackTokens
} from '#fluentui/react';
import './styles.css'
const TextFieldBasicExample = (props) => {
const [newItem, setNewItem] = useState({
title: ''
});
console.log('onchange', newItem);
const handleChange = (e) => {
setNewItem({
[e.target.name]: e.target.value
});
}
const addData = (event) => {
console.log('onclick', newItem);
props.callback(newItem);
}
return ( <
Stack horizontal >
<
Stack className = "add-inp" >
<
TextField label = "Add Item"
placeholder = "enter text"
name = "title"
onChange = {
handleChange
}
/> <
span id = "error_name" > < /span> <
PrimaryButton text = "Add"
className = "add-btn"
onClick = {
addData
}
/>
<
/Stack> <
/Stack>
);
};
export default TextFieldBasicExample
Drag Component JS file
import React, {
useState,
Component,
useEffect
} from "react";
import 'react-sortable-tree/style.css';
import {
removeNodeAtPath
} from 'react-sortable-tree';
import SortableTree from 'react-sortable-tree';
import {
toggleExpandedForAll
} from 'react-sortable-tree';
import './styles.css'
const Treeview = (props, reset) => {
console.log('props', props)
const initTreeData = [{
title: 'Data_1',
children: [{
title: "Data_2"
}]
},
{
title: 'Data_1'
},
{
title: 'Data_2'
}
]
console.log('test', initTreeData.length)
var test = {
index: initTreeData.length + 1,
title: props.info
}
useEffect(() => {
_fetchGeneral();
}, [])
const [treeData, setTreeData] = useState(initTreeData);
console.log(treeData, "*******")
if (test.title != '') {
var m = treeData.push(test)
// setTreeData(m);
}
const _fetchGeneral = async () => {
setTreeData(initTreeData);
}
const updateTreeData = (e) => {
setTreeData(e);
}
// Expand and collapse code
const expand = (expanded) => {
setTreeData(toggleExpandedForAll({
treeData: treeData,
expanded,
}), );
}
const expandAll = () => {
expand(true);
}
const collapseAll = () => {
expand(false);
}
// Expand and collapse code end
// remove node
const removeNode = (rowInfo) => {
let {
node,
treeIndex,
path
} = rowInfo;
setTreeData(removeNodeAtPath({
treeData: treeData,
path: path, // You can use path from here
getNodeKey: ({
node: TreeNode,
treeIndex: number
}) => {
console.log(number, 'event');
return (number);
},
ignoreCollapsed: false,
}))
}
// remove node end
return ( <
div style = {
{
display: 'flex',
flexDirection: 'column',
height: '100vh'
}
} >
<
div style = {
{
flex: '0 0 auto',
padding: '0 15px'
}
} >
<
h3 > Full Node Drag Theme < /h3> <
button onClick = {
expandAll
} > Expand All < /button> <
button onClick = {
collapseAll
} > Collapse All < /button> &
nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;
<
/div>
<
div style = {
{
flex: '1 0 50%',
padding: '0 0 0 15px'
}
} >
<
SortableTree className = "tree-dt"
id = "add_name"
treeData = {
treeData
}
onChange = {
updateTreeData
}
generateNodeProps = {
rowInfo => ({
buttons: [ <
div >
<
button label = 'Delete'
onClick = {
() => removeNode(rowInfo)
} > X < /button> <
/div>,
],
style: {
height: '50px',
}
})
}
canDrag = {
({
node
}) => !node.dragDisabled
}
/> <
/div> <
/div>
);
}
export default Treeview;
I have attached the output screen as well.

useState to update an object property

I am new to React. I have a functional component that is used to render am image and some properties of an image passed as props to the component. I would like to update the image source when there is an error rendering the image. I would also like to update the state property of the parent component and pass it back to the parent. I am not sure how to achieve the same. I have been struggling for so long to achieve this. Please can someone help me solve this issue. Many thanks in advance.
Parent Component:
import React, {
useState
} from 'react';
import PropTypes from 'prop-types';
import ImageRenderer from './ImageRenderer';
import VideoRenderer from './VideoRenderer';
const getComponent = {
'image': ImageRenderer,
'video': VideoRenderer
}
const AssetRenderer = (props) => {
console.log('props in asset ren:', props);
const [assetInfo, setAssetInfo] = useState(props);
console.log('assetInfo in parent:', assetInfo);
const isPublished = assetInfo.assetInfo.isAssetPublished;
let source = assetInfo.assetInfo.assetUrl;
const PreviewComponent = getComponent[assetInfo.assetInfo.type];
return ( < div > {
isPublished && source && < PreviewComponent assetInfo = {assetInfo} setAssetInfo = { setAssetInfo } />} </div>
);
}
AssetRenderer.propTypes = {
assetInfo: PropTypes.object.isRequired
};
export default AssetRenderer;
Child Component:
import React from 'react';
import PropTypes from 'prop-types';
import {
Subheading
} from '#contentful/forma-36-react-components';
const ImageRenderer = props => {
console.log('inside image renderer', props);
return ( <
div id = "asset-img" >
<
Subheading > Image preview: < /Subheading> <
p > Name: {
props.assetInfo.assetInfo.name
} < /p> <
p > Type: {
props.assetInfo.assetInfo.type
} < /p> <
p > Url: {
props.assetInfo.assetUrl
} < /p> <
img src = {
props.assetInfo.assetInfo.assetUrl
}
alt = "name"
onError = {
e => {
props.setAssetInfo(assetInfo => {
return { ...props.assetInfo.assetInfo,
assetUrl: 'https://example.com/404-placeholder.jpg',
isAssetPublished: false
} //would like to update the asset url to 404 and also set isAssetPublished to false and pass it back to parent to update parent state
});
}
}
/> <
/div>
)
}
ImageRenderer.propTypes = {
assetInfo: PropTypes.object.isRequired
};
export default ImageRenderer;
Instead of using new state in ImageRenderer component, you can just pass setState of Parent via props like this;
parent component
import React, { useStae } from 'react'
const parentCompoennt = props => {
const [assetInfo,setAssetInfo] = useState();
return (
<ImageRenderer assetInfo={assetInfo} setAssetInfo={setAssetInfo} />
);
}
imageRenderer component
const ImageRenderer = props => {
return(
<div id="asset-img">
<p> Name: {props.assetInfo.assetInfo.name} </p>
<p> Type: {props.assetInfo.assetInfo.type} </p>
<p> Url: {props.assetInfo.assetInfo.assetUrl} </p>
<img src={props.assetInfo.assetInfo.assetUrl} alt="name" onError={e => {
props.setAssetInfo(assetInfo => {
return { ...props.assetInfo, assetUrl: 'https://example.com/404-placeholder.jpg' } //would like to update the asset url to 404 and also set isAssetPublished to false and pass it back to parent to update parent state
});
}}/>
</div>
)
}
If the purpose is to handle image error only, then you can achieve it without re-rendering a component:
<img src={assetInfo.assetInfo.assetUrl} alt="name"
onError={e => {
e.target.src = 'https://example.com/404-placeholder.jpg';
}}
/>

Forwarding state vs forwarding props to child component

I have problem with forwarding state from parent to child component, these 2 components are class components.
When forwarding state from parent to child component, I want to use state showModal variable in child component as show as state variable:
this.state = {
show: this.props.show
}
This variable is being used to active the modal.
When I use it as this.props.show, the state has been forwarded to child component, and updated, but when I use props in this.state in child component it hasn't been updated. Has anyone idea where the problem is?
First - Parent component:
import React, { Component } from 'react';
import Modal from './UI/Modal';
class EnteredBooks extends Component {
constructor(props) {
super(props)
this.state = {
enteredBook: this.props.enteredBook,
showModal: false
}
}
detailsHandler = () => {
this.setState({
showModal: true
})
}
render() {
let show = this.state.showModal;
return (
<div>
<div className="product">
<img src="{this.props.enteredWatch.bookUrl}" />
<p>{this.props.enteredWatch.bookType}</p>
<p>euro{this.props.enteredWatch.bookPrice}</p>
<button
className="details-button"
onClick={this.detailsHandler}
>
Details
</button>
<Modal show={this.state.showModal} watch={this.state.enteredWatch} />
<button className="buy-button">Buy</button>
</div>
</div>
);
}
}
export default EnteredWatches;
Second - Child component:
import React, {Component} from 'react';
import classes from './Modal.css';
class Modal extends React.Component {
constructor(props) {
super(props)
this.state = {
book: this.props.book,
show: this.props.show
}
}
return(
<div>
<div className="Modal"
style={{
transform: this.state.show ? 'translateY(0)' : 'translateY(-100vh)',
opacity: this.state.show ? '1':'0'
}}>
<img src={this.state.book.bookUrl} />
<p>{this.state.book.bookType}</p>
<p>{this.state.book.watchUrl}</p>
<button className="details-button">Details</button>
<button className="buy-button">Buy</b
</div>
</div>
);
}
}
export default Modal;
constructor runs only once. It's suitable for calculating initial state. But this case is what getDerivedStateFromProps hook is for. It allows to calculate a state from props every time a component is updated, including initialization:
static getDerivedStateFromProps(props) {
return {
book: props.book,
show: props.show
};
}
I edited some parts of your code.I am not sure that you are asking this but hope it will enlight you for your problem.
First - Parent component:
import React, {
Component
} from 'react';
import Modal from './UI/Modal';
class EnteredBooks extends Component {
constructor(props) {
super(props)
this.state = {
enteredBook:"",
showModal: false
}
}
detailsHandler = () => {
this.setState({
showModal: true
enteredBook: this.props.enteredBook
})
}
render() {
let show = this.state.showModal;
return ( <
div >
<
div className = "product" >
<
img src = "{this.props.enteredWatch.bookUrl}" / >
<
p > {
this.props.enteredWatch.bookType
} < /p> <
p > euro {
this.props.enteredWatch.bookPrice
} < /p> <
button className = "details-button"
onClick = {
this.detailsHandler
} >
Details <
/button> <
Modal show = {
this.state.showModal
}
watch = {
this.state.enteredWatch
}
/> <
button className = "buy-button" > Buy < /button> <
/div> <
/div>
);
}
}
export default EnteredWatches;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Second - Child component:
import React, {
Component
} from 'react';
import classes from './Modal.css';
class Modal extends React.Component {
constructor(props) {
super(props)
this.state = {
book: "",
show: ""
}
}
componentWillReceiveProps(props) {
const {
book,
show
}: this.props;
if (book !== props.book || show !== props.show) {
this.setState({
book: props.book,
show: props.show
});
}
}
return ( <
div >
<
div className = "Modal"
style = {
{
transform: this.state.show ? 'translateY(0)' : 'translateY(-100vh)',
opacity: this.state.show ? '1' : '0'
}
} >
<
img src = {
this.state.book.bookUrl
}
/> <
p > {
this.state.book.bookType
} < /p> <
p > {
this.state.book.watchUrl
} < /p> <
button className = "details-button" > Details < /button> <
button className = "buy-button" > Buy < /b < /
div > <
/div>
);
}
}
PS : Also you can use componentWillReceiveProps(props) event for the
parent component.

How to setState from child component in React

I would like to set state of parent component from child component. I tried using props however its giving error Uncaught TypeError: this.props.setTopicClicked is not a function. And is there more efficient way for setting state of parent component instead of using props? I would like to set state of isTopicClicked: true
main-controller.jsx
import {React, ReactDOM} from '../../../build/react';
import SelectedTopicPage from '../selected-topic-page.jsx';
import TopicsList from '../topic-list.jsx';
import topicPageData from '../../content/json/topic-page-data.js';
export default class MainController extends React.Component {
state = {
isTopicClicked: false,
topicPageData
};
onClick(topicID) {
this.setState({
isTopicClicked: true,
topicsID: topicID
});
};
setTopicClicked(event){
this.setState({isTopicClicked: event});
};
render() {
return (
<div className="row">
{this.state.isTopicClicked
? <SelectedTopicPage topicsID={this.state.topicsID} key={this.state.topicsID} topicPageData={topicPageData}/>
: <TopicsList onClick={ this.onClick.bind(this) }/>}
</div>
);
}
};
selected-topic-page.jsx
import {React, ReactDOM} from '../../build/react';
import SelectedTopicPageMarkup from './selected-topic-page-markup.jsx';
import NextPrevBtn from './next-prev-btn.jsx';
export default class SelectedTopicPage extends React.Component {
state = {
topicPageNo: 0,
total_selected_topic_pages: 1
};
navigateBack(topicPageNo) {
if (this.state.topicPageNo > 0){
topicPageNo = this.state.topicPageNo - 1;
}
else {
topicPageNo = 0;
}
this.setState({topicPageNo : topicPageNo});
};
navigateNext(totalPagesInSelectedTopic) {
let topicPageNo;
if (totalPagesInSelectedTopic > this.state.topicPageNo + 1){
topicPageNo = this.state.topicPageNo + 1;
}
else if (totalPagesInSelectedTopic == this.state.topicPageNo + 1) {
this.props.setTopicClicked(true);
}
else {
topicPageNo = this.state.topicPageNo;
}
this.setState({topicPageNo : topicPageNo});
};
render() {
let topicsID = this.props.topicsID;
let topicPageNo = this.state.topicPageNo;
return (
<div>
{this.props.topicPageData.filter(function(topicPage) {
// if condition is true, item is not filtered out
return topicPage.topic_no === topicsID;
}).map(function (topicPage) {
let totalPagesInSelectedTopic = topicPage.topic_pages.length;
return (
<div>
<div>
<SelectedTopicPageMarkup headline={topicPage.topic_pages[0].headline} key={topicPage.topic_no}>
{topicPage.topic_pages[topicPageNo].description}
</SelectedTopicPageMarkup>
</div>
<div>
<NextPrevBtn moveNext={this.navigateNext.bind(this, totalPagesInSelectedTopic)} key={topicPage.topic_no} moveBack={this.navigateBack.bind(this, topicPageNo)}/>
</div>
</div>
);
}.bind(this))}
</div>
);
};
};
It seems you forgot to pass setTopicClicked to the child:
setTopicClicked={this.setTopicClicked.bind(this)}
Your <SelectedTopicPage /> does not contain setTopicClicked as props which results into the error
<SelectedTopicPage
topicsID={this.state.topicsID}
key={this.state.topicsID}
topicPageData={topicPageData}/>
You can try using a flux implementation to handle the state of your application and just pass props to the component. Otherwise, I think you're stuck in passing in setting the state using the component or its children.

Categories

Resources