I am trying to apply mapDispatchToProps instead of Dispatch action ,
but i got a error msg ,anyone help? Thabkyou
mapDispatchToProps() in Connect(ButtonGroup) must return a plain object. Instead received undefined.
Action creator
function clickDisable() {
return {
type: 'CLICK_DISABLE'
}
}
Component
var ButtonGroup = React.createClass({
clickAdd(event) {
this.props.dispatch(clickAdd());
} ,
clickSub(event) {
this.props.dispatch(clickSub());
} ,
/*clickDisable(event) {
this.props.dispatch(clickDisable());
} ,*/
render() {
const { age } = this.props;
return (
<ButtonToolbar style={{width: 17+ 'em'}} >
<Button id="search" style={{width: 5 + 'em'}}>{age}</Button>
<Button onClick={this.clickAdd} style={{width: 5 + 'em'}}>Createa</Button>
<Button onClick={this.props.clickDisable} style={{width: 5 + 'em'}}>Detele</Button>
</ButtonToolbar>
);
}
});
mapDispatchToProps
function mapDispatchToProps (dispatch) {
return Redux.bindActionCreators({
clickDisable: clickDisable
}, dispatch);
}
function mapStateToProps(state) {
return {
age: state.reducreForAge.age
}
}
Connect
const NewButtonGroup = connect(mapStateToProps,mapDispatchToProps)(ButtonGroup);
Use this in mapDispatchToProps. Everything else looks fine.
const mapDispatchToProps = dispatch => { return{
clickDisable: () => dispatch({type:'CLICK_DISABLE'})};};
Related
I am trying to learn react redux and I creating a small todo app, which has backend as REST server
most of the part is implemented, however, I am not able to understand how to pass a value from my input box to rest API call. I am able to successfully store the value of inputbox in redux state.
I am using react-thunk as middleware to handle API calls.
container
import { connect } from 'react-redux'
import {toggleAddTodoDialog, addNewTodo, handleChange} from '../actions'
import AddTodoDialog from '../components/add_todo_dialog';
class AddTodoContainer extends Component {
render() {
return (
<div>
<AddTodoDialog toggleAddTodoDialog={this.props.toggleAddTodoDialog}
addNewTodo = {this.props.addNewTodo}
newTodoList = {this.props.newTodoList}
handleChange = {this.props.handleChange}
is_add_todo_dialog_opened={this.props.is_add_todo_dialog_opened}/>
</div>
)
}
}
const mapStateToProps = (state) => {
return state
}
const bindActionsToDispatch = dispatch =>
(
{
toggleAddTodoDialog : (e) => dispatch(toggleAddTodoDialog(e)),
handleChange: (e) => dispatch(handleChange(e)),
addNewTodo : (e) => addNewTodo(e)
}
)
export default connect(mapStateToProps, bindActionsToDispatch)(AddTodoContainer)
component
export default class AddTodoDialog extends Component {
toggleAddTodoDialog = (e) => {
this.props.toggleAddTodoDialog(!this.props.is_add_todo_dialog_opened)
}
addNewTodo = (e) => {
this.props.addNewTodo()
this.toggleAddTodoDialog(e)
}
handleChange = (e) => {
this.props.handleChange(e)
}
render() {
return (
<div>
<Button color="primary" onClick={this.toggleAddTodoDialog}>Add new Todo</Button>
<Modal isOpen={this.props.is_add_todo_dialog_opened} >
{/* <Modal isOpen={false} > */}
<ModalHeader toggle={this.toggleAddTodoDialog}>Modal title</ModalHeader>
<ModalBody>
<FormGroup >
<Label for="Title">Task </Label>
<Input name="task"
value={this.props.newTodoList.task}
onChange={this.handleChange} />
</FormGroup>
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={this.addNewTodo}>OK</Button>{' '}
<Button color="secondary" onClick={this.toggleAddTodoDialog}>Cancel</Button>
</ModalFooter>
</Modal>
</div>
);
}
actions
export function addNewTodo() {
console.log("addNewTodo")
return function (dispatch) {
axios.post("http://localhost:5001/todos", 'task=' + this.props.addNewTodo.task)
.then(response => {
dispatch(_addTodoAction(response.data))
})
}
}
export function _addTodoAction(todos) {
console.log("_addTodoAction")
return {
type: 'ADD_TODO',
todos: todos
}
}
export function handleChange(event){
console.log("handleChange "+event)
return{
type: 'HANDLE_CHANGE',
event: event
}
}
reducer
case 'ADD_TODO':
console.log(action)
return {
...state,
todos: action.todos
}
You are dispatching like this:
addNewTodo : (e) => addNewTodo(e)
But your action doesn't take any arguments:
export function addNewTodo() {
console.log("addNewTodo")
return function (dispatch) {
axios.post("http://localhost:5001/todos", 'task=' + this.props.addNewTodo.task)
.then(response => {
dispatch(_addTodoAction(response.data))
})
}
}
If you want the value of the value of the input:
handleChange = (e) => {
this.props.handleChange(e.target.value)
}
And then dispatch:
handleChange: (value) => dispatch(handleChange(value)),
And then in the action:
export function handleChange(value) {
...
var rootReducer = Redux.combineReducers({
reducreForButtonGroup,reducreForButtonGroup2
});
i did set the initialState into the reducre as below, but the Detail button didnt disabled when i load the page .
var initialState2 = {
disabled:false
}
function reducreForButtonGroup2(state = initialState2, action) {
}
var DetailButton = React.createClass({
clickDisable(event) {
this.props.dispatch(clickDisable());
} ,
render() {
const { disable } = this.props;
return (
<ButtonToolbar style={{width: 17+ 'em'}}>
<Button disabled={disable} style={{width: 5 + 'em'}}>Detail</Button>
<Button onClick={this.clickDisable} style={{width: 5 + 'em'}}>Close</Button>
</ButtonToolbar>)
}
}) ;
function select(state) {
return {
disabled: state.disabled
}
}
const NewDetailButton = connect(select)(DetailButton);
New reducer
New reducer i want to add
var initialState = {
value:15
}
Action creators
function clickAdd() {
return {
type: 'CLICK_ADD'
}
}
New reducre
function reducreForButtonGroup(state = initialState, action) {
if (typeof state === 'undefined') {
return 0
}
var value;
switch(action.type) {
case 'CLICK_ADD': {
console.log("2");
return {
value: state.value + 1
}
}
default :{
return state
}
}
}
Component
var ButtonGroup = React.createClass({
clickAdd(event) {
this.props.dispatch(clickAdd());
} ,
render() {
const { value } = this.props;
return (
<ButtonToolbar style={{width: 17+ 'em'}} >
<Button id="search" style={{width: 5 + 'em'}}>{value}</Button>
<Button onClick={this.clickAdd} style={{width: 5 + 'em'}}>Create</Button>
</ButtonToolbar>
);
}
});
Mapstatetoprops i did use the reducre name in the mapStateToProps
function select(state) {
return {
value: state.reducreForButtonGroup.value
}
}
const NewButtonGroup = connect(select)(ButtonGroup);
you have spelling issue, it should be const { disabled } = this.props; and disabled={disabled} and I believe it should work.
Also log out what you get from the state because I believe it should be:
function select(state) {
console.log(state);
return {
disabled: state.reducreForButtonGroup2.disabled
}
}
One more think it is called a reducer not a reducre. You have it misspelled a few times.
This is surely very simple but I dont understand how it works. I try to bind checkbox with state and with state display different string. It is in React with Redux. The code below (bold font)
container:
class DropingList extends Component {
**conditionHandler() {
if(this.props.pet === 'cat'){
return "YEAH!!!"
}else {return null;}**
}
render() {
return (
<div>
<AddHimHer
click={this.props.onAddMan}
/>
{ this.props.pers.map(per =>(
<NewPerson
key={per.id}
click={() => this.props.onManDown(per.id)}
name={per.name}
age={per.age}
**animal={this.conditionHandler(this.props.pet)}**
/>
))
}
</div>
)
}
}
const mapStateToProps = state => {
return {
pers: state.persons
}
}
const mapDispatchToProps = dispatch => {
return {
onAddMan: (name,age,**pet**) => dispatch({type:actionTypes.ADD_MAN, data: {nam: name, ag: age, **superp: pet**}}),
onManDown: (id) => dispatch({type:actionTypes.MAN_DOWN, Id: id})
}
}
export default connect(mapStateToProps,mapDispatchToProps)(DropingList);
component:
const NewPerson = (props) => (
<div onClick={props.click}>
<h1>Is {props.name} a SUPERHERO? ? ???</h1>
<h2>He is {props.age} years old</h2>
**<h1>{props.animal}</h1>**
</div>
);
export default NewPerson;
reducer:
const initState = {
persons: []
}
const personReducer = (state = initState,action) => {
switch (action.type) {
case actionTypes.ADD_MAN:
const newMan = {
id: Math.random(),
name: action.data.nam,
age: action.data.ag,
**pet: action.data.superp**
};
return {
...state,
persons: state.persons.concat(newMan)
};
case actionTypes.MAN_DOWN:
return {
...state,
persons: state.persons.filter(person => person.id !== action.Id)
};
}
return state;
};
export default personReducer;
I am still newbe in React and Redux. I think I have ommited something.
Could you tell me whats wrong with my code?
Issue is pet is the part of the object (each object of the array), not a separate prop so you need to use per.pet in map callback function, like this:
{this.props.pers.map(per =>(
<NewPerson
key={per.id}
click={() => this.props.onManDown(per.id)}
name={per.name}
age={per.age}
animal={this.conditionHandler(per.pet)} // here
/>
))}
Now you are passing the pet value to function conditionHandler, so no need to use this.props.pet inside that directly use pet, like this:
conditionHandler(pet) {
if (pet === 'cat') {
return "YEAH!!!"
} else {
return null;
}
}
I am getting “TypeError: Cannot read property 'props' of undefined”
when i try to fire my function onChange from a deeper component. I can fire the action from the DemoForm component, but then I cannot pass in my value as it becomes undefined so, I am trying to make a function that takes in the event information and then I am firing my action but it says props is undefined, when I do a debugger and check on the console, its all there
// App.js
class App extends Component {
constructor(props) {
super(props)
}
handleThis(e){
this.props.SomeAction
}
render() {
return (
<div className="App">
<DemoForm state={this.props} someFunction={this.handleThis }/>
<AnotherForm/>
</div>
);
}
}
const mapStateToProps = (reduxState) => {
return reduxState;
}
const mapDispatchToProps = (dispatch) => {
return bindActionCreators(actionCreators, dispatch);
}
export default connect(
mapStateToProps, mapDispatchToProps
)(App)
// Demo.js
let DemoForm = ({ handleSubmit, submitting, state }) =>
<form onSubmit={handleSubmit(showResults)}>
<Field name="value" label="Value" component={RenderInput} onChange={(e) => this.props.someFunction(e.target.value) } />
<button type="submit"> Submit </button>
{console.log("Demo state >>>>> ", {state})}
</form>
DemoForm = reduxForm({
form: 'demo',
destroyOnUnmount: false,
validate
})(DemoForm)
export default DemoForm
// RenderInput
const RenderInput = createRenderer((input, label, onChange) => {
return <input {...input}/>
})
export default RenderInput
// createRenderer
const createRenderer = render => ({ input, meta, label, ...rest }) => {
return (
<div>
{/* <pre> {JSON.stringify(input, null, 2) }</pre> */}
<label> {label}</label>
{render(input, label, rest)}
{
meta.touched &&
<span className="text-danger"> {meta.error} </span>
}
</div>
)
}
export default createRenderer
// REDUCER
const initialState = {
todos: [],
count: 0,
demoPercent: 0,
anotherPercent : 0
}
export default function rootReducer(state = initialState, action) {
if(action.type === "INC"){
console.log("incrementing count")
let newState = {...state}
newState.count++
return {
...newState
}
}
if(action.type === "GET_PERCENT"){
console.log("getting balance percent", action.payload)
let newState = {...state}
newState.demoPercent = action.payload;
newState.anotherPercent = 100 - action.payload;
return {
...newState
}
}
return state;
}
// ACTION
export function increase(){
console.log("i am INC action firing")
return {
type: "INC"
}
}
export function getPercent(value){
console.log(value) //value is undefined
return {
type: "GET_PERCENT",
paypoad : value
}
}
You need to bind the handler in your controller, to do that just change your App component constructor to:
constructor(props) {
super(props);
this.handleThis = this.handleThis.bind(this);
}
You have to bind your function... And the best way to bind function is to use arrow functions
render() {
return (
<div className="App">
<DemoForm state={this.props} someFunction={(e) => this.handleThis(e)}/>
<AnotherForm/>
</div>
);
}
}
I have two react components which are ProgramSearchBox and DualBox which are generic and wrapper components of predefined npm packages AutoSuggest and DualListBox respectively.
My task to achieve is Based on the value from ProgramSearchBox, I have to list the set values in the DualListBox.
So, If user select a Program from ProgramSearchBox, then I will call API by passing the ProgramId and fetch the set of result values and have to bind them in the DualListBox.
I will get the user selected ProgramID from the ProgramSearchBox as a prop in DualBox component render method.
How to dispatch an action (call a function) from render function in DualBox component by passing the ProgramId?
If I call a method from render function in DualBox, that is becoming Infinite loop!
Here is DualBox component:
//DualBox.js
class DualBox extends React.Component {
constructor() {
super();
this.state = { selected: [] };
this.onChange = this.onChange.bind(this);
this.options = [ ];
}
onChange(selected) {
selected(selected);
}
updateOptions()
{
console.log("Update Option method called :" + this.props.traineesList );
this.options = [{ value: 'luna', label: 'Moon' }, { value: 'phobos', label: 'Phobos' }];
//this.options = this.props.traineeList.map( (value,id) => )
}
render() {
const {ProgramID} = this.props; // HERE I GET ProgramID AS PROP FROM AN ANOTHER COMPONENT
const {selected} = this.state;
if(ProgramID !== "") // BASED ON THIS ProgramID VALUE, I NEED TO DISPATCH AN ACTION.
{
{this.updateProgramId(ProgramID)} // THIS IS CAUSING INFINITE LOOP
{this.updateOptions}
console.log("Program Id came to dualbox:" +ProgramID);
return <DualListBox options={this.options} selected={selected} onChange={this.onChange}
canFilter
filterCallback={(option, filterInput) => {
if (filterInput === '') {
return true;
}
return (new RegExp(filterInput, 'i')).test(option.label);
}}
filterPlaceholder="Filter..."
/>;
}
else
{
console.log("Program Id didn't come to dualbox");
return <DualListBox options={this.options} selected={selected} onChange={this.onChange}
canFilter
filterCallback={(option, filterInput) => {
if (filterInput === '') {
return true;
}
return (new RegExp(filterInput, 'i')).test(option.label);
}}
filterPlaceholder="Filter..."
/>;
}
}
}
function mapStateToProps(state, ownProps) {
return {
traineesList: state.traineesList
};
}
const mapDispatchToProps = (dispatch, ownProps) => {
return {
updateProgramId: bindActionCreators(( {ProgramID}) => dualBoxActions.getTraineesList(ProgramID), dispatch)
};
}
export default connect(mapStateToProps,mapDispatchToProps)(DualBox);
Here is the ProgramSearchBox component:
function renderSuggestion(suggestion) {
return (
<ul>
<li>{suggestion.Program}</li>
</ul>
);
}
class ProgramSearchBox extends React.Component {
constructor(props) {
super(props);
}
render() {
const { value, suggestions, onChange, onSuggestionSelected} = this.props;
const inputProps = {
placeholder: "Look Up",
value,
onChange: (event, { newValue, method }) => {
this.setState({
value: newValue
});
console.log("onChange: " + JSON.stringify(newValue) );
onChange(newValue);
}
};
return (
<Autosuggest
suggestions={suggestions}
onSuggestionsFetchRequested={this.props.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.props.onSuggestionsClearRequested}
onSuggestionSelected={
(event, { suggestion, suggestionValue, suggestionIndex, sectionIndex, method }) => {
console.log("onSuggestionSelected: " + JSON.stringify(suggestion) );
onSuggestionSelected(suggestion);
}
}
getSuggestionValue={(suggestion) => suggestion.Program}
renderSuggestion={renderSuggestion}
inputProps={inputProps}
theme={theme}
/>
);
}
}
function mapStateToProps(state, ownProps) {
return {
suggestions: state.results
};
}
const mapDispatchToProps = (dispatch, ownProps) => {
return {
onSuggestionsFetchRequested: bindActionCreators(({ value }) => searchActions.getProgramSuggestions(value), dispatch),
onSuggestionsClearRequested: bindActionCreators(() => searchActions.clearSuggestions(), dispatch),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(ProgramSearchBox);
Don't call other functions in render() method. Render method is responsible only for rendering views, it can be called many times and it should be as pure as possible.
Updated answer (2019-11-21)
Use componentDidUpdate(prevProps) lifecycle function to react to prop changes.
It will look something like this:
componentDidUpdate(prevProps) {
if (this.props.ProgramID !== '' && prevProps.ProgramID !== this.props.ProgramID) {
this.updateProgramId(this.props.ProgramID)
}
}
Old answer
To do actions depending on props changing, use componentWillReceiveProps(nextProps) lifecycle function.
It will look something like this:
componentWillReceiveProps(nextProps) {
if (nextProps.ProgramID !== '' && this.props.ProgramID !== nextProps.ProgramID) {
this.updateProgramId(ProgramID)
}
}
After calling this.updateProgramId(ProgramID) props will update and render method will be called.
More info about ReactJS lifecycle:
https://facebook.github.io/react/docs/react-component.html#componentwillreceiveprops