I have a situation here where there are two components. the first component have an input field and the other component is displaying icon and a title name. i used Redux toolkit where when the input field is written anything the icon is changed to another icon. the problem is the icon doesn't change i don't why so, i want to know how to define the icon in the component as shown in my code below.
import { createSlice } from "#reduxjs/toolkit";
const initialState = {
icon: "unTick",
value: "",
};
const tickSlice = createSlice({
name: "tickSign",
initialState,
reducers: {
setValue: (state, action) => {
state.value = action.payload;
},
changeIcon: (state) => {
if (state.icon === "unTick") {
state.icon = "tick";
} else {
state.icon = "unTick";
}
},
},
});
export const { changeIcon, setValue } = tickSlice.actions;
export const selectValue = (state) => state.value;
export const selectIcon = (state) => state.icon;
export default tickSlice.reducer;
the code of the first component of input
const value = useSelector(selectValue);
const dispatch = useDispatch();
const handleChange = useCallback(
(e) => {
dispatch(setValue(e.target.value));
if (e.target.value.trim().length !== 0) {
dispatch(changeIcon("tick"));
} else {
dispatch(changeIcon("unTick"));
}
},
[dispatch]
);
<input
id="boxInput"
type="text"
value={value}
placeholder="Enter Apprenticeship Title"
onChange={handleChange}
/>
the code for the icon change component
import React from "react";
import { useDispatch, useSelector } from "react-redux";
import { selectIcon } from "../features/TickSlice";
import { TickCircle, UnTickCircle } from "./IconSvg";
const DescNavTitles = ({ title }) => {
const icon = useSelector(selectIcon);
const dispatch = useDispatch();
return (
<div className="navTitle">
<svg>{icon === "tick" ? <TickCircle /> : <UnTickCircle />}</svg>
<p className="navTitle__name">{title}</p>
</div>
);
};
export default DescNavTitles;
So, i don't know where exactly should i define the icon to be displayed and changed dynamically
components on the page
Redux tool when written
Redux tool when not written
You are not selecting the icon state correctly. Based on the screen captures showing the Redux state from the devtools your root state has two properties, appren and tick.
The Redux state:
{
appren: {
boxItems: [....],
title: "",
position: "",
...
},
tick: {
icon: "....",
value: "...."
},
}
I'm assuming it's this state.tick that is the tickSign slice you are working with. Recall that the useSelector hook callback is passed the entire Redux state object.
const icon = useSelector(state => state.tick.icon);
The selectValue and selectIcon selector functions need to access the correct path to access the expected state properties.
export const selectValue = (state) => state.tick.value;
export const selectIcon = (state) => state.tick.icon;
Additionally, the changeIcon action is only toggling the state value when dispatched, but it seems you want to conditionally set the value from an input element's onChange handler. I suggest the following refactor:
const initialState = {
isTicked: false,
value: "",
};
const tickSlice = createSlice({
name: "tickSign",
initialState,
reducers: {
setValue: (state, action) => {
state.value = action.payload;
},
changeIsTicked: (state, action) => {
state.isTicked = action.payload;
},
},
});
export const { changeIcon, setValue } = tickSlice.actions;
export const selectValue = (state) => state.tick.value;
export const selectIsTicked = (state) => state.tick.isTicked;
const value = useSelector(selectValue);
const dispatch = useDispatch();
const handleChange = useCallback((e) => {
dispatch(setValue(e.target.value));
dispatch(changeIsTicked(e.target.value.trim().length !== 0));
}, [dispatch]);
<input
id="boxInput"
type="text"
value={value}
placeholder="Enter Apprenticeship Title"
onChange={handleChange}
/>
const DescNavTitles = ({ title }) => {
const isTicked = useSelector(selectIsTicked);
return (
<div className="navTitle">
<svg>{isTicked ? <TickCircle /> : <UnTickCircle />}</svg>
<p className="navTitle__name">{title}</p>
</div>
);
};
The isTicked state is actually unnecessary though as it can be completely derived from the state.tick.value state value. You can create a selector function that computes this. Example:
const initialState = {
value: "",
};
const tickSlice = createSlice({
name: "tickSign",
initialState,
reducers: {
setValue: (state, action) => {
state.value = action.payload;
},
},
});
export const { changeIcon, setValue } = tickSlice.actions;
export const selectValue = (state) => state.tick.value;
export const selectIsTicked = (state) => !!state.tick.value.length;
const DescNavTitles = ({ title }) => {
const isTicked = useSelector(selectIsTicked);
return (
<div className="navTitle">
<svg>{selectIsTicked ? <TickCircle /> : <UnTickCircle />}</svg>
<p className="navTitle__name">{title}</p>
</div>
);
};
In the component's render method, you are using the useSelector hook to get the current icon value from the state and conditionally render either TickCircle or UnTickCircle component based on the icon value.
const icon = useSelector(selectIcon);
return (
<div className="navTitle">
<svg>{icon === "tick" ? <TickCircle /> : <UnTickCircle />}</svg>
<p className="navTitle__name">{title}</p>
</div>
);
Try this.
Related
I want to remove an object from an array onClick when I click on the delete button, I write some code in my redux and react as well and it is not working !!
My reducers
import { ActionsTypes } from "../Constant/ActionsTypes";
argument(state, action)
const initialState = {
events : [],
days : (""),
};
export const SetEventReducer = (state = initialState, action) => {
switch(action.type) {
case ActionsTypes.SET_EVENTS :
return {... state, events : action.payload};
default :
return state;
}
};
export const trackDaysReducer = (state= initialState, action) => {
switch(action.type) {
case ActionsTypes.TRACK_DAYS:
return {... state, days : action.payload}
default :
return state;
}
};
export const removeEventReducer = (state = initialState,action) => {
switch(action.type) {
case ActionsTypes.REMOVE_EVENT :
return {}
default :
return state;
}
};
Event array represents my state array
My Event Component who contains the button
import React from 'react'
import { useSelector, useDispatch } from 'react-redux';
import { RemoveEvent } from '../Redux/Actions/ActionsEvents';
const Event = () => {
const events = useSelector((state)=> state.allEvents.events);
const removeEvents = useSelector((state)=> state.removeEvents);
const dispatch = useDispatch();
const removeEventHandler = () => {
dispatch(RemoveEvent({}))
}
return (
<section>
{events.map((singleEvent)=> {
const {title, id, day} = singleEvent;
return (
<article className="event-pop-up" key={id} >
<h1>The Name of your Event is <span>{title}</span></h1>
<button className="btn event"
onClick={removeEventHandler}>
Delete Event</button>
</article>
)
})}
</section>
)
}
export default Event;
RemoveEventAction
export const RemoveEvent = (id) => {
return {
type : ActionsTypes.REMOVE_EVENT,
};
};
This is the link to check out the app : https://boring-boyd-ecbeee.netlify.app/
What do you think? Thanks
Try this code :
in yours reducers :
export const removeEventReducer = (state = initialState,action) => {
switch(action.type) {
case ActionsTypes.REMOVE_EVENT :
return {... state, events : state.events.filter((event) => event.id !== action.payload)} // payload = id in this case
default :
return state;
}
then in your Event Component who contains the button :
import React from 'react'
import { useSelector, useDispatch } from 'react-redux';
import { RemoveEvent} from '../Redux/Actions/ActionsEvents';
const Event = () => {
const events = useSelector((state)=> state.allEvents.events);
const removeEvents = useSelector((state)=> state.removeEvents);
const dispatch = useDispatch();
const removeEventHandler = (id) => {
dispatch(RemoveEvent(id))
}
return (
<section>
{events.map((singleEvent)=> {
const {title, id, day} = singleEvent;
return (
<article className="event-pop-up" key={id} >
<h1>The Name of your Event is <span>{title}</span></h1>
<button className="btn event"
onClick={()=> removeEventHandler(id)}>
Delete Event</button>
</article>
)
})}
</section>
)
}
export default Event;
then in your RemoveEvent action
export const RemoveEvent = (id) => {
return {
type : ActionsTypes.REMOVE_EVENT, payload: id
};
};
You can remove an event using it's id.
const removeEventHandler = (id) => {
dispatch(RemoveEvent(id))
}
return (
<section>
{events.map((singleEvent)=> {
const {title, id, day} = singleEvent;
return (
<article className="event-pop-up" key={id} >
<h1>The Name of your Event is <span>{title}</span></h1>
<button className="btn event"
onClick={() => removeEventHandler(id)}>
Delete Event</button>
</article>
)
})}
</section>
After passing this id to the reducer you can loop through the events array and remove this event from array and set the new state.
And in your reducers, you can use filter() method to remove a particular event having that id
export const removeEventReducer = (state = initialState, action) => {
switch(action.type) {
case ActionsTypes.REMOVE_EVENT :
return {... state, events : state.events.filter((event) => event.id !== action.payload)}
default :
return state;
}
}
For Remove Event Action:
export const RemoveEvent = (id) => {
return {
type : ActionsTypes.REMOVE_EVENT,
payload: id,
};
};
I have multiple forms and buttons which user can edit now I would like to display a button save if the state of redux changes.
live demo : display button save when the state changes
Here is my redux.
const initialState = {
firstName: "Kunta ",
lastName: "Kinte",
age: 35,
country: "Ghana",
color: "#000"
};
const DetailsReducer = (state = initialState, action) => {
const { name, value } = action;
return { ...state, [name]: value };
};
export default DetailsReducer;
Here is my js code to show save button if there is a change in redux state
import React, { useState, useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
const Settings = () => {
const fields = useSelector((state) => state);
const dispatch = useDispatch();
const [saveBtn, setSaveBtn] = useState(false);
useEffect(() => {
setSaveBtn(true); // show save btn if there is changes in state
}, []);
console.log(fields.firstName);
return (
<div>
<div>
<h1>Edit </h1>
First Name:{" "}
<input
name="firstname"
value={fields.firstName}
onChange={(e) =>
dispatch({ name: "firstName", value: e.target.value, type: "" })
}
/>
{saveBtn === true && <button className="btn-save">save </button>}
</div>
</div>
);
};
export default Settings;
[1]: https://codesandbox.io/s/multiple-inputs-kkm6l?file=/src/Components/Settings.js:0-816
What do I need to do to solve this problem.?
Did you try this ?
const fields = useSelector((state) => state.WHATEVER_REDUCER);
useEffect(() => {
setSaveBtn(true); // show save btn if there is changes in state
}, [fields]);
You can try something like this:
<input
name="firstname"
value={fields.firstName}
onChange={(e) =>
dispatch({ name: "firstName", value: e.target.value, type: "" }, setSaveBtn(true))
}
/>
While also removing:
useEffect(() => {
setSaveBtn(true); // show save btn if there is changes in state
}, []);
You can do it like this. Remove effect hook, move setSaveBtn to input onChange and after you click save, just set setSaveBtn to false.
import React, { useState, useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
const Settings = () => {
const fields = useSelector((state) => state);
const dispatch = useDispatch();
const [saveBtn, setSaveBtn] = useState(false);
console.log(fields.firstName);
return (
<div>
<div>
<h1>Edit </h1>
First Name:{" "}
<input
name="firstname"
value={fields.firstName}
onChange={(e) => {
dispatch({ name: "firstName", value: e.target.value, type: "" })
setSaveBtn(true)
}}
/>
{saveBtn === true &&
<button
onClick={() => setSaveBtn(false)}
className="btn-save">save </button>}
</div>
</div>
);
};
export default Settings;
I'm trying to reselect middleware today and prevent unnecessary re-rendering.
Here is my reducer.js:
const INITIAL_STATE = {
dogs: 100,
cats: 12312302384
};
const pets = (state = INITIAL_STATE, action) => {
switch (action.type) {
case "CHANGE_DOGS":
return {
...state, dogs: state.dogs + 1
};
case "CHANGE_CATS":
return {
...state, cats: state.cats + 1
};
default:
return { ...state };
}
};
export default pets;
Here is my main.js:
import React from "react";
import { createSelector } from "reselect";
import { useSelector, useDispatch } from "react-redux";
function ReduxDeneme(props) {
// Selectors - Classic style - NON Memoized!
// const dogsData = useSelector(state => state.pets.dogs);
// const catsData = useSelector(state => state.pets.cats);
// Dogs rendering.. --> First opening..
// 10:11:28.070 index.js:18 CATS rendering.. --> First opening..
// 10:11:29.703 index.js:13 Dogs rendering.. --> Press "ChangeDogs" button.
// 10:11:29.703 index.js:18 CATS rendering.. --> Press "ChangeDogs" button.
// 10:11:33.143 index.js:13 Dogs rendering.. --> Press "ChangeCats" button.
// 10:11:33.143 index.js:18 CATS rendering.. --> Press "ChangeCats" button.
// Selectors - Memoized version RESELECT middleware'i ile..
const dogsDataMemo = createSelector(
state => state.pets.dogs,
dogs => dogs
);
const catsDataMemo = createSelector(
state => state.pets.cats,
cats => cats
);
const dogsData = useSelector(dogsDataMemo)
const catsData = useSelector(catsDataMemo)
// Components
const Dogs = ({ dogsData }) => {
console.log("Dogs rendering..");
return <h1>{dogsData}</h1>;
};
const Cats = ({ catsData }) => {
console.log("Cats rendering..");
return <h1>{catsData}</h1>;
};
// Actions
const dispatch = useDispatch();
const changeDogs = () => dispatch({ type: "CHANGE_DOGS" });
const changeCats = () => dispatch({ type: "CHANGE_CATS" });
return (
<div>
<Dogs dogsData={dogsData} />
<Cats catsData={catsData} />
<button onClick={changeDogs}>Change Dogs</button>
<button onClick={changeCats}>Change CATS</button>
</div>
);
}
export default ReduxDeneme;
1. Scenario: I have used classic, non-memoized style selectors. There are 6 times console.log on the console. (2 times at first opening - it's normal -, 2 times at clicking "Change Dogs" button, 2 times at clicking "Change Cats" button) . It means 6 times re-rendering happened.
2. Scenario: I have used reselect middleware to prevent unnecessary re-renderings. But, It doesn't work or I have misunderstood meaning of reselect.
Could someone explain the correct way or where am I doing wrong?
You are defining the selectors inside your component. You should do it outside (e.g. somewhere near your reducer).
Currently you are recreating the selector after each render. Here's a better way:
// inside reducer.js
export const petsSel = state => state.pets;
export const dogsDataMemo = createSelector(
petsSel,
pets => pets.dogs
);
export const catsDataMemo = createSelector(
petsSel,
pets => pets.cats
);
Added a codesandbox with a working example based on your code: https://codesandbox.io/s/delicate-snowflake-5ssrw
To achieve what you desire you need to use React.memo (https://reactjs.org/docs/react-api.html#reactmemo) as well:
const Dogs = React.memo(({ dogsData }) => {
console.log("Dogs rendering..");
return <h1>{dogsData}</h1>;
});
First of all, I thank #tudor very much for his efforts. All of his said is correct.
But, I want to show that Reselect works.
SCENARIO 1 - NON-MEMOIZED
import React, { memo } from "react";
import { useSelector, useDispatch } from "react-redux";
// import { catsDataMemo, dogsDataMemo } from "./selectors";
// Components
const Dogs = memo(({ dogsData }) => {
console.log("Dogs rendering..");
return <h1>{dogsData}</h1>;
});
const Cats = memo(({ catsData }) => {
console.log("Cats rendering..");
return <h1>{catsData}</h1>;
});
function ReduxDeneme() {
// Standart useSelector without MEMOIZED
const dogsData = useSelector(
state => state.pets.dogs,
console.log("dogsData Selector çalıştı.")
);
const catsData = useSelector(
state => state.pets.cats,
console.log("catsData Selector çalıştı.")
);
// Actions
const dispatch = useDispatch();
const changeDogs = () => dispatch({ type: "CHANGE_DOGS" });
const changeCats = () => dispatch({ type: "CHANGE_CATS" });
const noChangeCats = () =>
dispatch({ type: "NO_CHANGE_CATS", payload: catsData });
return (
<div>
<Dogs dogsData={dogsData} />
<Cats catsData={catsData} />
<button onClick={changeDogs}>Change Dogs</button>
<button onClick={changeCats}>Change CATS</button>
<button onClick={noChangeCats}>No Change</button>
</div>
);
}
export default memo(ReduxDeneme);
Be Careful! When you click the "Change Dogs" button, output in the console will be:
dogsData Selector çalıştı.
catsData Selector çalıştı.
Dogs rendering..
or when you click the "Change Cats" button, the output will be:
dogsData Selector çalıştı.
catsData Selector çalıştı.
Cats rendering..
Whatever button you press, both of useSelectors will work as you can see from console.log
SCENARIO 2 - MEMOIZED WITH RESELECT MIDDLEWARE
Firstly, we separate memoized selectors to another file as #tudor.gergely mentioned.
BE CAREFULL! You must define the correct path of an object.
// selectors.js
import { createSelector } from "reselect";
export const dogsDataMemo = createSelector(
state => state.pets.dogs, // BE CAREFULL while defining..
dogs => {
console.log("DogsDataMemo has worked.");
return dogs;
}
);
export const catsDataMemo = createSelector(
state => state.pets.cats, // BE CAREFULL while defining..
cats => {
console.log("CatsDataMemo has worked.");
return cats;
}
);
Then, we import this file into main.js file and use again useSelector with our memoized selectors:
import React, { memo } from "react";
import { useSelector, useDispatch } from "react-redux";
import { catsDataMemo, dogsDataMemo } from "./selectors";
// Components
const Dogs = memo(({ dogsData }) => {
console.log("Dogs rendering..");
return <h1>{dogsData}</h1>;
});
const Cats = memo(({ catsData }) => {
console.log("Cats rendering..");
return <h1>{catsData}</h1>;
});
function ReduxDeneme() {
const dogsData = useSelector(dogsDataMemo);
const catsData = useSelector(catsDataMemo);
// Actions
const dispatch = useDispatch();
const changeDogs = () => dispatch({ type: "CHANGE_DOGS" });
const changeCats = () => dispatch({ type: "CHANGE_CATS" });
const noChangeCats = () =>
dispatch({ type: "NO_CHANGE_CATS", payload: catsData });
return (
<div>
<Dogs dogsData={dogsData} />
<Cats catsData={catsData} />
<button onClick={changeDogs}>Change Dogs</button>
<button onClick={changeCats}>Change CATS</button>
<button onClick={noChangeCats}>No Change</button>
</div>
);
}
export default memo(ReduxDeneme);
and the final output:
When click the "Change Dogs" button:
DogsDataMemo has worked.
Dogs rendering
When click the "Change Cats" button:
CatsDataMemo has worked.
Cats rendering..
I have to use useDispatch() for my toggle buttons so I have to refractor them from react to redux state. I was following the tutorial of basics of Redux and I think I have done that properly but when I try to at least useSelector to display the redux'state of button it doesnt show anything.
So here is my code:
// types.js in actions folder
export const TOGGLE = "TOGGLE";
// buttonActions in actions folder
export const toggle = () => {
return {
type: 'TOGGLE'
};
};
// buttonReducer in reducers folder
const buttonReducer = (state = true, action) => {
switch(action.type) {
case 'TOGGLE':
return !state;
default:
return state;
};
};
export default buttonReducer;
And the buttonReducer is imported into combineReducers which go to store.
The component code:
import React, { useState, useEffect } from 'react'
import isloff from './mainpage_imgs/isloff.png'
import islon from './mainpage_imgs/islon.png'
import PropTypes from "prop-types";
import { connect, useDispatch, useSelector } from "react-redux";
import { toggle } from '../../actions/buttonActions'
const Islbutton = props => {
const [open, setOpen] = useState(true);
const [role, setRole] = useState('');
useEffect(() => {
if (props.auth.user)
{
setRole(props.auth.user.role);
}
}, []);
const test = useSelector(state => state.button);
const checkRole = (role) => {
if (role === 'Menager' || role === 'Technolog')
{
return true }
else
{
return false
};
}
const toggleImage = () => {
if(checkRole(role)) {
setOpen(!open)
};
}
const getImageName = () => open ? 'islOnn' : 'islOfff'
const dispatch = useDispatch();
return(
<div>
<img style={islplace} src={open ? islon : isloff }
onClick={()=> dispatch(toggle())} />
</div>
);
}
Islbutton.propTypes = {
button: PropTypes.func.isRequired,
auth: PropTypes.obj.isRequired
};
const mapStateToProps = state => ({
button: state.button,
auth: state.auth
});
export default connect(mapStateToProps, {}), (Islbutton);
Based on your latest comments and my understanding of your use case I may suggest following distilled approach:
//dependencies
const { render } = ReactDOM,
{ createStore } = Redux,
{ connect, Provider } = ReactRedux
//action creators
const SET_ROLE = 'SET_ROLE',
MANAGER_APPROVED = 'MANAGER_APPROVED',
setRole = role => ({type:SET_ROLE, role}),
mngAppr = () => ({type:MANAGER_APPROVED})
//initial state, reducer, store
const initialState = {role:'Technolog', approved:false},
appReducer = (state=initialState, action) => {
switch(action.type){
case SET_ROLE : {
const {role} = state,
{role: newRole} = action
return {...state, role: newRole}
}
case MANAGER_APPROVED : {
const {approved} = state
return {...state, approved: !approved}
}
default: return state
}
},
store = createStore(appReducer)
//ui component to emulate toggling roles
const SwitchRoles = ({currentRole, switchRole}) => (
<div>
<label><input type="radio" name="role" value="Manager" onChange={e => switchRole(e.target.value)} />Manager</label>
<label><input type="radio" name="role" value="Technolog" onChange={e => switchRole(e.target.value)} />Technolog</label>
</div>
)
//connect radio buttons click to togling roles action
const mapDispatch = dispatch => ({switchRole: role => dispatch(setRole(role))}),
SwitchRolesContainer = connect(null,mapDispatch)(SwitchRoles)
//ui component to toggle 'approved' within global state
const ToggleApprove = ({onApprove,isManager}) => (
<button onClick={onApprove} disabled={!isManager}>Toggle</button>
)
//connect onToggle handler to dispatching 'toggle' action
const mapStateToProps = ({role}) => ({isManager: role == 'Manager'}),
mapDispatchToProps = dispatch => ({onApprove: () => dispatch(mngAppr())}),
ToggleApproveContainer = connect(mapStateToProps, mapDispatchToProps)(ToggleApprove)
//ui component to display current state of 'open'
const IsApproved = ({isApproved}) => <div>{isApproved ? 'Approved by manager' : 'Not approved by manager'}</div>
//attach isOpen prop to global 'open' variable
const mapState = ({approved}) => ({isApproved: approved}),
IsApprovedContainer = connect(mapState)(IsApproved)
//render the app
render (
<Provider store={store}>
<SwitchRolesContainer />
<IsApprovedContainer />
<ToggleApproveContainer />
</Provider>,
document.getElementById('root')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.1.3/react-redux.min.js"></script><div id="root"></div>
Hopefully, it gives a piece of mind about toggling global variables and mapping their values onto local components state.
Before I was doing this with local state but now I need to pass it to Redux. I am not using Redux Forms and I am not going to.
This is how I was/am doing it with local state using the useState hook:
const DynamicInputExample = () => {
const [addShareStructureInput, setAddShareStructureInput] = useState({
inputs: ['input-0'],
});
const appendInput = () => {
const newInput = `input-${addShareStructureInput.inputs.length}`;
setAddShareStructureInput(prevState => ({ inputs: prevState.inputs.concat([newInput]) }));
};
return(
<div id="dynamicInput">
// HERE I MAP THE INPUTS
{addShareStructureInput.inputs.map(input => (
<FormField
key={input}
onChange={() => onChange()}
/>
))}
<div>
<Button
type="button"
// HERE I CALL THE FUNCTION TO ADD NEW INPUT
onClick={() => appendInput()}
>
+ Add More
</Button>
</div>
</div>
)
}
But now I need to remove that hook on that code and make the same logic on Redux.
This is what I've done so far:
Action:
export const shareStructureInputsAction = shareStructureInputs => ({
type: ActionTypes.SHARE_STRUCTURE_INPUTS,
payload: { shareStructureInputs },
});
Reducer:
const initialState = {
shareStructureInputs: ['input-0'],
}
const handlers = {
[ActionTypes.SHARE_STRUCTURE_INPUTS](state, action) {
return {
...state,
// BELOW I NEED TO ADD THE LOGIC TO KEEP THE STATE OF THE INPUTS ADDED
shareStructureInputs: {
...action.payload.shareStructureInputs,
},
};
},
}
So, how can I simulate the same logic/behavior with Redux instead?
It's possible to do it, using something like this:
const initialState = {
shareStructureInputs: ['input-0'],
}
const handlers = {
[ActionTypes.SHARE_STRUCTURE_INPUTS](state, action) {
return {
...state,
shareStructureInputs: [...shareStructureInputs, action.payload.shareStructureInputs],
};
},
}
Action:
export const shareStructureInputsAction = shareStructureInputs => ({
type: ActionTypes.SHARE_STRUCTURE_INPUTS,
payload: shareStructureInputs
});
Reducer:
const initialState = {
shareStructureInputs: ['input-0'],
}
const handlers = {
[ActionTypes.SHARE_STRUCTURE_INPUTS](state, action) {
return {
...state,
shareStructureInputs: action.payload
};
},
}
import { connect } from 'react-redux';
import { shareStructureInputsAction } from 'actions/shareStructureInputsAction';
const DynamicInputExample = (props) => {
const { shareStructureInputs, setShareStructureInputs } = props;
const appendInput = () => {
const newInput = `input-${shareStructureInputs.length}`;
setShareStructureInputs(shareStructureInputs.concat(newInput));
};
return(
<div id="dynamicInput">
// HERE I MAP THE INPUTS
{shareStructureInputs.map(input => (
<FormField
key={input}
onChange={() => onChange()}
/>
))}
<div>
<Button
type="button"
// HERE I CALL THE FUNCTION TO ADD NEW INPUT
onClick={() => appendInput()}
>
+ Add More
</Button>
</div>
</div>
)
}
const mapStateToProps((state) => ({
shareStructureInputs: state[ActionTypes.SHARE_STRUCTURE_INPUTS].shareStructureInputs
}));
const mapDispatchToProps({
setShareStructureInputs: shareStructureInputsAction
})
export default connect(mapStateToProps, mapDispatchToProps)(DynamicInputExample);