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.
Related
onclick of button need to add state value -12 with 50 and the result value ie(38) should update to destcommomRowData value and srcommomRowData value should become 0
is it possible to achieve
any suggestion?
please refer below snippet
import React, { useState } from "react";
const Test = () => {
let srcommomRowData = {
2020: { firstLevel: { children: { secondLevel: { value: -12 } } } },
};
let destcommomRowData = {
2020: { firstLevel: { children: { secondLevel: { value: 50 } } } },
};
const [srcdata, srcsetData] = useState(srcommomRowData);
const [destdata, destsetData] = useState(destcommomRowData);
const updateData = () => {};
return (
<div>
<div>Testing</div>
<div>
<button onClick={updateData}> Click </button>
</div>
</div>
);
};
export default Test;
First off, you shouldn't need to update variables. You should update state only - the only reason why you might want to store the initial data in variables outside of state is for readability.
To accomplish your goal, you need to set destination data state value using the src data state value, and then update the src data state value:
const Test = () => {
const srcCommomRowData = {
2020: { firstLevel: { children: { secondLevel: { value: -12 } } } }
};
const destCommomRowData = {
2020: { firstLevel: { children: { secondLevel: { value: 50 } } } }
};
const [srcData, setSrcData] = useState(srcCommomRowData);
const [destData, setDestData] = useState(destCommomRowData);
const updateData = () => {
setDestData(destData => {
const updatedData = { ...destData };
updatedData[2020].firstLevel.children.secondLevel.value +=
srcData[2020].firstLevel.children.secondLevel.value;
return updatedData;
});
setSrcData(srcData => {
const updatedData = { ...srcData };
updatedData[2020].firstLevel.children.secondLevel.value = 0;
return updatedData;
});
};
return (
<div>
<div>Testing</div>
<br />
<div>
<button onClick={updateData}> Click </button>
</div>
</div>
);
};
<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>
See this working example that displays the values: https://codesandbox.io/s/condescending-franklin-e1l9s?file=/src/Test.js:42-1213
I am trying to understand redux with the help of an online todo application resource.
However, I can't seem to figure out where does the 'todos' reducer get the initial state from ?
I've consoled the state but can't seem to wrap my head around it ?
After the initial render, state is consoled 3 times as,
[ ]
[ ]
[ state object ]
Link: 'https://codepen.io/iamrkcheers/pen/rNNoBvB'
Any help is appreciated.
Thank You.
// --------- actions start ----------
const ADD_TODO = "ADD_TODO";
const TOGGLE_TODO = "TOGGLE_TODO";
const SET_VISIBILITY_FILTER = "SET_VISIBILITY_FILTER";
const VisibilityFilters = {
SHOW_ALL: "SHOW_ALL",
SHOW_COMPLETED: "SHOW_COMPLETED",
SHOW_ACTIVE: "SHOW_ACTIVE"
};
let nextTodoId = 3;
function addTodo(text) {
return {
type: ADD_TODO,
id: nextTodoId++,
text
}
}
function toggleTodo(id) {
return {
type: TOGGLE_TODO,
id
}
}
function setVisibilityFilter(filter) {
return {
type: SET_VISIBILITY_FILTER,
filter
}
}
// --------- actions end ----------
// --------- reducers start ----------
function todos(state = [], action) {
console.log('state is:',state);
switch (action.type) {
case ADD_TODO: {
return [...state, {
text: action.text,
completed: false,
id: action.id
}];
}
case TOGGLE_TODO: {
return state.map((todo, id) => {
if (id === action.id) {
return Object.assign({}, todo, {
completed: !todo.completed
});
}
return todo;
});
}
default: {
return state;
}
}
}
function visibilityFilter(state = VisibilityFilters.SHOW_ALL, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER: {
return action.filter;
}
default: {
return state
}
}
}
const todoApp = Redux.combineReducers({
visibilityFilter,
todos
});
// --------- reducers end ----------
// --------- components start ----------
const App = () => {
const getDate = date => new Date(date);
const days = ["Воскресенье", "Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота"];
return (
<div className="block">
<div className="info-date">
<div className="date">{ getDate(Date.now()).toLocaleDateString("ru") }</div>
<div className="day">{ days[getDate(Date.now()).getDay()] }</div>
</div>
<AddTodo />
<Footer />
<VisibleTodoList />
</div>
);
};
const Footer = () => {
return (
<div className="filters">
<FilterLink filter="SHOW_ALL">Все задачи</FilterLink>
<FilterLink filter="SHOW_ACTIVE">Активные</FilterLink>
<FilterLink filter="SHOW_COMPLETED">Завершенные</FilterLink>
</div>
);
};
const Link = ({ active, children, onClick }) => {
if (active) {
return <span className="filter-item non-active">{ children }</span>
}
return (
<a className="filter-item" href="#" onClick = { event => {
event.preventDefault();
onClick();
} }>{ children }</a>
);
};
const Todo = ({ onClick, completed, text }) => {
const styles = {
textDecoration: completed ? "line-through" : "none"
};
return (
<li onClick = { onClick } style = { styles }>
<a>{ text }</a>
</li>
);
};
const TodoList = ({ todos, onTodoClick }) => {
return (
<div className="list">
<ul>
{
todos.map(todo => <Todo
key = { todo.id } { ...todo }
onClick = { () => onTodoClick(todo.id) } />)
}
</ul>
</div>
);
};
// --------- components end ----------
// --------- containers start ----------
let AddTodo = ({ dispatch }) => {
let input;
return (
<div>
<form className="addForm" onSubmit = { event => {
event.preventDefault();
if (!input.value.trim()) {
return;
}
dispatch(addTodo(input.value));
input.value = "";
} }>
<input type="text" placeholder="Что нужно сделать?" ref = { node => input = node }/>
<button type="submit" className="btn"></button>
</form>
</div>
);
};
AddTodo = ReactRedux.connect()(AddTodo);
var mapStateToProps = (state, ownProps) => {
return {
active: ownProps.filter === state.visibilityFilter
};
};
var mapDispatchToProps = (dispatch, ownProps) => {
return {
onClick: () => {
dispatch(setVisibilityFilter(ownProps.filter));
}
};
};
const FilterLink = ReactRedux.connect(
mapStateToProps,
mapDispatchToProps
)(Link);
const getVisibleTodos = (todos, filter) => {
switch (filter) {
case "SHOW_ALL": {
return todos;
}
case "SHOW_COMPLETED": {
return todos.filter(todo => todo.completed);
}
case "SHOW_ACTIVE": {
return todos.filter(todo => !todo.completed);
}
default: {
return todos;
}
}
};
var mapStateToProps = state => {
return {
todos: getVisibleTodos(state.todos, state.visibilityFilter)
}
};
var mapDispatchToProps = dispatch => {
return {
onTodoClick: id => {
dispatch(toggleTodo(id));
}
};
};
const VisibleTodoList = ReactRedux.connect(
mapStateToProps,
mapDispatchToProps
)(TodoList);
// --------- containers end ----------
// --------- application start ----------
const initialState = {
visibilityFilter: "SHOW_ALL",
todos: [
{
id: 0,
text: "Изучить React",
completed: true
},
{
id: 1,
text: "Изучить Redux",
completed: true
},
{
id: 2,
text: "Написать приложение \"Список задач\"",
completed: false
}
]
};
let store = Redux.createStore(todoApp, initialState);
ReactDOM.render(
<ReactRedux.Provider store = { store }>
<App />
</ReactRedux.Provider>,
document.querySelector("#root")
);
// --------- application end ----------
You are defining the initial state right here :
function todos(state = [], action) {
Generally, while defining reducers, we also define initialState(state = [] in your case) , which is the state that goes into the reducer till we populate it with data (from an external source like api, or user input).
You can read more on initial state here : https://redux.js.org/recipes/structuring-reducers/initializing-state#initializing-state
there are two ways where you can define initial state;
the first one is in your reducer where you did function
todos(state = [], action) and ,
the second is when you create the store, you can pass initial state as a second argument in your createStore function. In your case , you have a second argument when you create your store which is an array of three todos which you can see when you console log it. store = Redux.createStore(todoApp, initialState), here the reducer gets this initial state
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'})};};
I'm trying to set Loader when data is not fetched yet. This scenario would be easy if the data would be uploaded only ones (logic here: set a flag is isFetching to true, when receiving from redux set it to false). But my scenario is a bit different. I'd like to get my data multiple times to update my Calendar component. All is done thru redux with axios package.
It looks like that:
My reducer adds isFetching flag when my axios request is done(the store is updated):
import { ACTIVE_MONTH } from "../actions/types";
export default function(state = null, action){
switch(action.type){
case ACTIVE_MONTH:
return Object.assign({}, state, {
isFetching: false,
fullyBooked: action.payload
})
default:
return state;
}
}
And the component looks like that:
import React, { Component } from 'react';
import Calendar from 'react-calendar';
import ChooseHour from './ChooseHour';
import { connect } from 'react-redux';
import * as actions from '../actions';
class Calendario extends Component {
state = { showHours: false, disabledDates: null}
componentDidMount() {
const { chosenRoom } = this.props;
const date = new Date();
const reqMonth = date.getMonth() + 1;
const reqYear = date.getFullYear();
this.props.activeMonthYearToPass({reqMonth, reqYear, chosenRoom});
}
onChange = date => this.setState({ date }, () => {
const { chosenRoom, isBirthday } = this.props;
const year = date.getFullYear();
const month = date.getMonth() + 1;
const day = date.getDate();
const fullDate = `${year}/${month}/${day}`;
const roomAndDayObj = {fullDate, chosenRoom, isBirthday};
this.props.sendRoomAndDay(roomAndDayObj);
}
);
onClickDay(e) {
const { chosenRoom } = this.props;
!chosenRoom ? this.setState({ errorMsg: "Wybierz pokój", showHours: false}) :
this.setState({ showHours: true, errorMsg:'' });
}
passActiveDate(activeDate) {
const { chosenRoom } = this.props;
const reqMonth = activeDate.getMonth() + 1;
const reqYear = activeDate.getFullYear();
this.setState({ pending: true},
() => this.props.activeMonthYearToPass({reqMonth, reqYear, chosenRoom})
);
this.props.passDateDetails({reqMonth, reqYear});
}
render() {
const { fullyBookedDays, isBirthday } = this.props;
const { errorMsg, pending } = this.state;
return (
<div>
<div className="calendarsCont">
<Calendar
onChange={this.onChange}
onClickDay={(e) => this.onClickDay(e)}
onActiveDateChange={({ activeStartDate }) => this.passActiveDate(activeStartDate)}
value={this.state.date}
locale="pl-PL"
tileDisabled={({date, view}) =>
(view === 'month') &&
fullyBookedDays && fullyBookedDays.fullyBooked.some(item =>
date.getFullYear() === new Date(item).getFullYear() &&
date.getMonth() === new Date(item).getMonth() -1 &&
date.getDate() === new Date(item).getDate()
)}
/>
}
</div>
<p style={{color: 'red'}}>{errorMsg}</p>
<div>
{this.state.showHours ?
<ChooseHour chosenDay={this.state.date} chosenRoom={this.props.chosenRoom} isBirthday={isBirthday}/> :
null}
</div>
</div>
)
}
}
function mapStateToProps({fullyBookedDays}){
return {
fullyBookedDays,
}
}
export default connect (mapStateToProps, actions)(Calendario);
So the new values will come many times from axios request.
What kind of strategy do you use in that case?
THANK YOU!
Whenever there is multiple fetching requests, or even multiple actions that indicates something async is happening and needs to be stored in a part of the state, I use a counter :
export default function(state = {fetchCount: 0}, action){
switch(action.type){
case FETCHING_THING:
return Object.assign({}, state, {
fetchCount: state.fetchCount + 1
})
case FETCHING_THING_DONE:
return Object.assign({}, state, {
fetchCount: state.fetchCount - 1,
fullyBooked: action.payload
}
default:
return state;
}
}
Then you can just check fetchCount > 0 in your mapstatetoprops.
function mapStateToProps({fullyBookedDays, fetchCount}){
return {
fullyBookedDays,
isLoading: fetchCount > 0
}
}
Please take below as an example , Redux-thunk style action is used for wrap multiple axios requests and dispatch them all.
//axios call2
function getData1() {
return axios.get('/data1');
}
//axios call2
function getData2() {
return axios.get('/data2');
}
//redux-thunk action creator
function getFullData() {
return (dispatch, getState) => {
axios.all([getData1(), getData2()])
.then(axios.spread(function (acct, perms) {
//call normal action creator
dispatch(fetchData1())
dispatch(fetchData2())
}));
};
}
//normal actioncreator
function fetchData1(data)
{
return {type: "FETCH_DATA1", payload: data}
}
//normal actioncreator
function fetchData2(data)
{
return {type: "FETCH_DATA2", payload: data}
}
//reducer1:
function reducer1 (state = defaultedState ,action){
return Object.assign({},{...state, data: action.payload, isFetching: false} )
}
//reducer2:
function reducer2 (state = defaultedState ,action){
return Object.assign({},{...state, data: action.payload, isFetching: false} )
}
//component:
mapStateToProps = function(state){
return {
data1: state.data1.data,
data2: state.data2.data,
isFetching1: state.data1.isFetching,
isFetching2: state.data2.isFetching
}
}
import React, { Component } from "react";
class MyComponent extends Component{
render(){
return (!data1 && isFetching1) || (!data2 && isFetching2) ? <Loading> : <DataComponent>
}
}
connect(mapStateToProps)(MyComponent)
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;
}
}