React redux, each child component is getting the same value from redux - javascript

I have a parent component that maps out a list of child component. Each child component has their own props like name, id, etc. I want to persist the checkbox is checked value on page refresh so I am using redux for storage. When I check/uncheck an individual checkbox, all the others checkboxes also gets checked/unchecked.
component file
const Daily = ({ daily, character_id }) => {
const { dailyName, id } = daily;
const checkbox = useSelector(checkboxSelector)
const dispatch = useDispatch()
const deleteDaily = async (id) => {
const dailyDoc = doc(db, `charactersDailies/${character_id}/dailies`, id)
await deleteDoc(dailyDoc)
window.location.reload(false)
}
return (
<div className="dailyContainer">
<input
className="dailyCheckbox"
id={daily.id}
type="checkbox"
checked={checkbox}
onChange={() => dispatch(setCheckboxAction(daily.id))}
/>
<label className="dailyName" htmlFor={daily.id}>
<h4 className="dailyName">{dailyName.toUpperCase()}</h4>
</label>
<h4 className="deleteDailyBTN" onClick={() => deleteDaily(id)}>
✕
</h4>
</div>
)
};
export default Daily;
redux action file
export const setCheckboxAction = (id) => {
return {
type: 'SET-CHECKBOX',
payload: id
}
};
redux reducer file
const checkboxReducer = (state = null, action) => {
switch(action.type) {
case 'SET-CHECKBOX':
return !state
default:
return state
}
};
export default checkboxReducer;
How do I only access each individual checkbox state from redux instead of all of the checkboxes?

Issue
All the checkboxes are using the same single checked state since it's a boolean.
export const setCheckboxAction = (id) => {
return {
type: 'SET-CHECKBOX',
payload: id
}
};
const checkboxReducer = (state = null, action) => {
switch(action.type) {
case 'SET-CHECKBOX':
return !state // <-- single boolean state
default:
return state
}
};
const Daily = ({ daily, character_id }) => {
...
const checkbox = useSelector(checkboxSelector); // <-- single state
...
return (
<div className="dailyContainer">
<input
...
checked={checkbox} // <-- single state
...
/>
...
</div>
)
};
Solution
Use the passed id payload of the setCheckboxAction action to conditionally toggle a specific checkbox by id. Convert the checkbox state to an object/map that uses the passed id as a computed property and toggles the truthy/falsey value. The UI will check the value using daily.id.
Example:
const checkboxReducer = (state = {}, action) => {
switch(action.type) {
case 'SET-CHECKBOX':
return {
...state,
[action.payload]: !state[action.payload], // payload -> id
};
default:
return state;
}
};
const Daily = ({ daily, character_id }) => {
const { dailyName, id } = daily;
const checkboxes = useSelector(checkboxSelector);
const dispatch = useDispatch();
const deleteDaily = async (id) => {
const dailyDoc = doc(db, `charactersDailies/${character_id}/dailies`, id)
await deleteDoc(dailyDoc)
window.location.reload(false)
}
return (
<div className="dailyContainer">
<input
className="dailyCheckbox"
id={daily.id}
type="checkbox"
checked={checkboxes[daily.id]}
onChange={() => dispatch(setCheckboxAction(daily.id))}
/>
<label className="dailyName" htmlFor={daily.id}>
<h4 className="dailyName">{dailyName.toUpperCase()}</h4>
</label>
<h4 className="deleteDailyBTN" onClick={() => deleteDaily(id)}>
✕
</h4>
</div>
);
};

Related

State not updating components (React)

I have made a weather app using redux. Now, when the app loads the state is changed. But when I try to change the city name it doesn't changes.
How can I change the state from another component?
React.useEffect(() => {
fetchLocation();
}, [lat, long])
const fetchLocation = async () => {
await fetch("https://geolocation-db.com/json/")
.then(res => res.json())
.then(result => {
scity(result.city);
console.log(ccity);
dispatch(changeCity(ccity));
fetchWeatherByCity(result.city);
});
}
const fetchWeatherByCity = async (city) => {
await fetch(`${REACT_APP_API_URL}/weather/?q=${city}&units=metric&APPID=${REACT_APP_API_KEY}`)
.then(res => res.json())
.then(result => {
setData(result);
console.log(result);
setState({});
});
};
dispatch(changeCity(ccity)); // changing the city
// It is the current city, when the app loads
//First component
//changeCity action file
export const changeCity = (t) =>{
return({
type: "CHANGE_CITY",
payload: t,
});
}
//reducer file
const cn = (state = "", action) => {
switch (action.type) {
case "CHANGE_CITY":
return state = action.payload;
default:
return state;
}
}
export default cn;
// second component from where I want to change the city
<TextInput onChangeText={(value) => st(value)} style={styles.search_bar} placeholder="Another Location" placeholderTextColor="#fff"></TextInput>
<Button onPress = {()=> dispatch(changeCity(t))}></Button>
// this is second component
Now, I want to update the city name from the second component.
You need return state instead of return assign value to state
case "CHANGE_CITY":
return action.payload;

Redux Toolkit State issue when sending to child component

I am creating react redux application using redux toolkit and I'm passing some props to child component, it supposed to be one post because I'm using a map in parent component and passing one data to each component.
I'm trying to do Edit button and when clicking the "Edit button" trying to send ID to redux store but there is an error. If anyone know the answer please let me know.
Below is my redux slice:
import { createAsyncThunk, createSlice } from "#reduxjs/toolkit";
import axios from "axios";
const initialState = {
allPosts: [],
loading: "idle",
error: "",
currentId: "",
};
export const fetchAlltAsync = createAsyncThunk(
"allposts",
async (_, thunkAPI) => {
try {
const response = await axios.get("http://localhost:5000/posts/");
// The value we return becomes the `fulfilled` action payload
return response.data;
} catch (error) {
throw thunkAPI.rejectWithValue({ error: error.message });
}
}
);
export const postsingleAsync = createAsyncThunk(
"postsingleAsync",
async (post, { dispatch }) => {
const response = await axios.post("http://localhost:5000/posts/", post);
return response.data;
}
);
export const idsingleAsync = createAsyncThunk(
"idsingleAsync",
async (id, updatedpost) => {
const response = await axios.patch(
`http://localhost:5000/posts/${id}`,
updatedpost
);
return response.data;
}
);
export const postSlice = createSlice({
name: "posts",
initialState,
// The `reducers` field lets us define reducers and generate associated actions
reducers: {
// Use the PayloadAction type to declare the contents of `action.payload`
newsetcurrentId: (state, action) => {
state.currentId = action.payload;
},
},
// The `extraReducers` field lets the slice handle actions defined elsewhere,
// including actions generated by createAsyncThunk or in other slices.
extraReducers: (builder) => {
builder.addCase(fetchAlltAsync.pending, (state) => {
state.allPosts = [];
state.loading = "Loading";
});
builder.addCase(fetchAlltAsync.fulfilled, (state, action) => {
state.allPosts = action.payload;
state.error += "Loaded";
});
builder.addCase(fetchAlltAsync.rejected, (state, action) => {
state.allposts = "data not loaded";
state.loading = "error";
state.error = action.error.message;
});
builder.addCase(idsingleAsync.fulfilled, (state, action) => {
state.currentId = action.payload;
});
},
});
export const { setcurrentId, newsetcurrentId } = postSlice.actions;
// The function below is called a selector and allows us to select a value from
// the state. Selectors can also be defined inline where they're used instead of
// in the slice file. For example: `useSelector((state: RootState) => state.counter.value)`
export const selectCount = (state) => state.counter.value;
// We can also write thunks by hand, which may contain both sync and async logic.
// Here's an example of conditionally dispatching actions based on current state.
export const incrementIfOdd = (amount) => (dispatch, getState) => {};
export default postSlice.reducer;
Below is my parent component:
import React, { useEffect, useState } from "react";
import Post from "./Post";
import { useSelector, useDispatch } from "react-redux";
const Posts = ({ SETCURRENTID, CURENTID }) => {
// const dispatch = useDispatch();
const posts = useSelector((state) => state.posts.allPosts);
return (
<div>
{posts &&
posts.map(({ _id, ...rest }) => (
<Post key={_id} rest={rest} id={_id} />
))}
</div>
);
};
export default Posts;
This is my child component:
import React from "react";
import moment from "moment";
import { idsingleAsync, newsetcurrentId } from "../../features/postSlice";
import { useSelector, useDispatch } from "react-redux";
const Post = ({ rest, _id }) => {
const dispatch = useDispatch();
console.log(rest, "gff");
//const { id } = this.rest._id;
const handleClick = () => dispatch(newsetcurrentId());
return (
<div>
<h1>{rest.title}</h1>
<img
style={{ maxWidth: "250px", border: "12px solid purple" }}
alt="d"
src={rest.selectedFile}
/>
<h2>{moment(rest.createdAt).fromNow()}</h2>
<button onClick={() => dispatch(newsetcurrentId(rest._id))}> edit</button>
<h5>{rest.tags.map((tag) => `#${tag} `)}</h5>
<h5 onClick={() => {}}>{rest.likeCount}</h5>
<button onClick={() => {}}>Delete</button>
</div>
);
};
export default Post;
This is the redux error:
requestId(pin):undefined
TL;DR
Instead of rest._id , try passing the id prop to your newsetcurrentId dispatch:
const Post = ({ rest, id }) => { //Change _id to id
const dispatch = useDispatch();
const handleClick = () => dispatch(newsetcurrentId());
return (
<div>
<h1>{rest.title}</h1>
<img
style={{ maxWidth: "250px", border: "12px solid purple" }}
alt="d"
src={rest.selectedFile}
/>
<h2>{moment(rest.createdAt).fromNow()}</h2>
{/* pass id here */}
<button onClick={() => dispatch(newsetcurrentId(id))}> edit</button>
<h5>{rest.tags.map((tag) => `#${tag} `)}</h5>
<h5 onClick={() => {}}>{rest.likeCount}</h5>
<button onClick={() => {}}>Delete</button>
</div>
);
};
Explanation
When you are doing this destructuring:
posts.map(({ _id, ...rest }) => ( your rest object will actually contain all the post properties apart from _id so you don't actually have rest._id which you are trying to access on your Post child.
Additionally, you are passing id={_id} as a prop from the parent to the child, so you don't actually have an _id prop on your Post component (change it to id).

Translating context to redux with setTimeout

I have this context:
interface AlertContextProps {
show: (message: string, duration: number) => void;
}
export const AlertContext = createContext<AlertContextProps>({
show: (message: string, duration: number) => {
return;
},
});
export const AlertProvider: FC<IProps> = ({ children }: IProps) => {
const [alerts, setAlerts] = useState<JSX.Element[]>([]);
const show = (message: string, duration = 6000) => {
let alertKey = Math.random() * 100000;
setAlerts([...alerts, <Alert message={message} duration={duration} color={''} key={alertKey} />]);
setTimeout(() => {
setAlerts(alerts.filter((i) => i.key !== alertKey));
}, duration + 2000);
};
return (
<>
{alerts}
<AlertContext.Provider value={{ show }}>{children}</AlertContext.Provider>
</>
);
};
which I need to "translate" into a redux slice. I got a hang of everything, apart from the show method. What would be the correct way to treat it? I was thinking about a thunk, but it's not really a thunk. Making it a reducer with setTimeout also seems like an ugly thing to do. So how would you guys do it?
My code so far:
type Alert = [];
const initialState: Alert = [];
export const alertSlice = createSlice({
name: 'alert',
initialState,
reducers: {
setAlertState(state, { payload }: PayloadAction<Alert>) {
return payload;
},
},
});
export const { setAlertState } = alertSlice.actions;
export const alertReducer = alertSlice.reducer;
The timeout is a side effect so you could implement that in a thunk.
You have an action that shows an alert message that has a payload of message, id and time to display, when that time runs out then the alert message needs to be removed so you need a remove alert message action as well that is dispatched from the thunk with a payload of the id of the alert message.
I am not sure why you add 2 seconds to the time to hide the message duration + 2000 since the caller can decide how long the message should show I don't think it should half ignore that value and randomly add 2 seconds.
Here is a redux example of the alert message:
const { Provider, useDispatch, useSelector } = ReactRedux;
const { createStore, applyMiddleware, compose } = Redux;
const initialState = {
messages: [],
};
//action types
const ADD_MESSAGE = 'ADD_MESSAGE';
const REMOVE_MESSAGE = 'REMOVE_MESSAGE';
//action creators
const addMessage = (id, text, time = 2000) => ({
type: ADD_MESSAGE,
payload: { id, text, time },
});
const removeMessage = (id) => ({
type: REMOVE_MESSAGE,
payload: id,
});
//id generating function
const getId = (
(id) => () =>
id++
)(1);
const addMessageThunk = (message, time) => (dispatch) => {
const id = getId();
dispatch(addMessage(id, message, time));
setTimeout(() => dispatch(removeMessage(id)), time);
};
const reducer = (state, { type, payload }) => {
if (type === ADD_MESSAGE) {
return {
...state,
messages: state.messages.concat(payload),
};
}
if (type === REMOVE_MESSAGE) {
return {
...state,
messages: state.messages.filter(
({ id }) => id !== payload
),
};
}
return state;
};
//selectors
const selectMessages = (state) => state.messages;
//creating store with redux dev tools
const composeEnhancers =
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
reducer,
initialState,
composeEnhancers(
applyMiddleware(
//simple implementation of thunk (not official redux-thunk)
({ dispatch }) =>
(next) =>
(action) =>
typeof action === 'function'
? action(dispatch)
: next(action)
)
)
);
const App = () => {
const messages = useSelector(selectMessages);
const dispatch = useDispatch();
return (
<div>
<button
onClick={() =>
dispatch(addMessageThunk('hello world', 1000))
}
>
Add message
</button>
<ul>
{messages.map((message) => (
<li key={message.id}>{message.text}</li>
))}
</ul>
</div>
);
};
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/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.2.0/react-redux.min.js"></script>
<div id="root"></div>
#HMR's use of a thunk is fine, but I don't like what they've done to your reducer. You're already using redux-toolkit which is great! redux-toolkit actually includes and exports a nanoid function which they use behind the scenes to create unique ids for thunks. You can use that instead of Math.random() * 100000.
I always start by thinking about types. What is an Alert? You don't want to store the <Alert/> because a JSX.Element is not serializable. Instead you should just store the props. You'll definitely store the message and key/id. If you handle expiration on the front-end then you would also store the duration, but if the expiration is handled by a thunk then I don't think you need it in the redux state or component props.
It seems like you want to allow multiple alerts at one time, so return payload is not going to cut it for your reducer. You'll need to store an array or a keyed object will all of your active alerts.
You absolute should not use setTimeout in a reducer because that is a side effect. You can use it either in a thunk or in a useEffect in the Alert component. My inclination is towards the component because it seems like the alert should probably be dismissible as well? So you can use the same function for handling dismiss clicks and automated timeouts.
We can define the info that we want to store for each alert.
type AlertData = {
message: string;
id: string;
duration: number;
}
And the info that we need to create that alert, which is the same but without the id because we will generate the id in the reducer.
type AlertPayload = Omit<AlertData, 'id'>
Our state can be an array of alerts:
const initialState: AlertData[] = [];
We need actions to add a new alert and to remove an alert once it has expired.
import { createSlice, PayloadAction, nanoid } from "#reduxjs/toolkit";
...
export const alertSlice = createSlice({
name: "alert",
initialState,
reducers: {
addAlert: (state, { payload }: PayloadAction<AlertPayload>) => {
const id = nanoid(); // create unique id
state.push({ ...payload, id }); // add to the state
},
removeAlert: (state, { payload }: PayloadAction<string>) => {
// filter the array -- payload is the id
return state.filter((alert) => alert.id !== payload);
}
}
});
export const { addAlert, removeAlert } = alertSlice.actions;
export const alertReducer = alertSlice.reducer;
So now to the components. What I have in mind is that you would use a selector to select all of the alerts and then each alert will be responsible for its own expiration.
export const AlertComponent = ({ message, duration, id }: AlertData) => {
const dispatch = useDispatch();
// function called when dismissed, either by click or by timeout
// useCallback is just so this can be a useEffect dependency and won't get recreated
const remove = useCallback(() => {
dispatch(removeAlert(id));
}, [dispatch, id]);
// automatically expire after the duration, or if this component unmounts
useEffect(() => {
setTimeout(remove, duration);
return remove;
}, [remove, duration]);
return (
<Alert
onClose={remove} // can call remove directly by clicking the X
dismissible
>
<Alert.Heading>Alert!</Alert.Heading>
<p>{message}</p>
</Alert>
);
};
export const ActiveAlerts = () => {
const alerts = useSelector((state) => state.alerts);
return (
<>
{alerts.map((props) => (
<AlertComponent {...props} key={props.id} />
))}
</>
);
};
I also made a component to create alerts to test this out and make sure that it works!
export const AlertCreator = () => {
const dispatch = useDispatch();
const [message, setMessage] = useState("");
const [duration, setDuration] = useState(8000);
return (
<div>
<h1>Create Alert</h1>
<label>
Message
<input
type="text"
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
</label>
<label>
Duration
<input
type="number"
step="1000"
value={duration}
onChange={(e) => setDuration(parseInt(e.target.value, 10))}
/>
</label>
<button
onClick={() => {
dispatch(addAlert({ message, duration }));
setMessage("");
}}
>
Create
</button>
</div>
);
};
const App = () => (
<div>
<AlertCreator />
<ActiveAlerts />
</div>
);
export default App;
Code Sandbox Link

react wait for component to get state from store then render child

i am using react redux. In this Profiles component i use this.startSetUsers in componentWillMount to trigger fetching users from the mongodb and store it to state. then i mapStateToProps to get users from state as props, so i get this.props.users.
Then in render i try to pass users as props to ProfilesList component and render that or if no users render no users from Profile component.
Now this works if state is empty and i navigate to ProfilesList or if i refresh that particular url with ProfilesList component but if go to ProfilePage component where through startSetUser i get only one user in state and show it in ProfilePage and then from that component where now state has one user i try to go to ProfilesList via < Link> show all users < /Link> i get an error "this.props.users.map is not a function" and i guess that is because mapStateToProps isnt finished before rendering and using this.props.users.map
i hope someone understood me, this was a bit mouthful. :D is there some work around this?
class Profiles extends React.Component {
componentWillMount() {
this.props.startSetUsers()
}
render() {
return(
<div className="content-container content-container--list">
<div className="list-header">
<div className="show-for-mobile">Users</div>
<div className="show-for-desktop">User</div>
<div className="show-for-desktop">Joined</div>
</div>
<div className="list-body">
{this.props.users.length === 0 ? (
<div className="list-item list-item--message">
<span>No users</span>
</div>
) : (
this.props.users.map((user) => {
return <ProfilesList key={user._id} {...user} />
})
)
}
</div>
</div>
)
}
}
const mapStateToProps = (state) => ({
users: state.user
})
export default connect(mapStateToProps, actions)(Profiles)
ProfilesList
const ProfilesList = ({ username, email, created_at, avatar }) => (
<Link className="list-item" to={`/user/${username}`}>
<div className="list-item__avatar-title">
<div className="list-item__avatar">
<img src={avatar || 'https://image.ibb.co/bUv8k7/NoImage.png'} />
</div>
<div>
<h3 className="list-item__title">{username}</h3>
<span className="list-item__sub-title">{email}</span>
<br />
<span className="list-item__sub-title">List of articles: X</span>
</div>
</div>
<div>
<h4 className="list-item__joined">{moment(created_at).format('MMMM Do, YYYY')}</h4>
</div>
</Link>
)
export default ProfilesList;
Profile
class Profile extends React.Component {
constructor(props) {
super(props);
this.state = {
usersLoaded: false
};
}
componentWillMount() {
this.props.startSetUser(this.props.match.params.id)
}
render() {
return(
<div>
{
this.props.user && this.props.match.params.id ?
<ProfilePage {...this.props}/> :
<div>404</div>
}
</div>
)
}
}
const mapStateToProps = (state, props) => ({
user: state.user
})
export default connect(mapStateToProps, actions)(Profile)
ProfilePage
const ProfilePage = props => {
const {user} = props;
return (
<div className="section">
<div className="profile__header">
<div className="profile__name">
<h2>{`${user.firstName} ${user.lastName}`}</h2>
<h3>{user.email}</h3>
</div>
</div>
<div className="profile__content">
<div className="photo-container">
<div className="photo">
<img src={user.avatar || 'https://image.ibb.co/bUv8k7/NoImage.png'} />
</div>
</div>
<div className="profile__info">
<p>Username: <strong>{user.username}</strong></p>
<li>Email: <strong>{user.email}</strong></li>
<li>Location: <strong>{user.location}</strong></li>
<li>City: <strong>{user.city || renderMessage}</strong></li>
<li>Birthday: <strong>{`${user.day} ${user.month}, ${user.year}`}</strong></li>
<li>Gender: <strong>{user.gender}</strong></li>
<li>Joined: <strong>{moment(user.created_at).format('MMMM Do, YYYY')}</strong></li>
</div>
<div className="profile__button">
<button className="button">Edit</button>
<Link to="/users" className="button">List of all users</Link>
</div>
</div>
</div>
);
};
export default ProfilePage;
reducer
const userReducer = (state = [], action) => {
switch (action.type) {
case 'SET_USER':
return action.user;
case 'SET_USERS':
return action.users;
default:
return state;
}
};
export default userReducer;
edit: forgot to post action
// SET_USER
export const setUser = (user) => ({
type: 'SET_USER',
user
})
export const startSetUser = (username) => {
return (dispatch) => {
axios.get(`${ROOT_URL}/user/${username}`)
.then(response => {
dispatch(setUser(response.data))
})
}
}
// SET_USERS ALL
export const setUsers = (users) => ({
type: 'SET_USERS',
users
})
export const startSetUsers = () => {
return (dispatch) => {
axios.get(`${ROOT_URL}/users`)
.then(response => {
dispatch(setUsers(response.data))
})
}
}
Looks like it's an issue with your reducer:
const userReducer = (state = [], action) => {
switch (action.type) {
case 'SET_USER':
return action.user;
case 'SET_USERS':
return action.users;
default:
return state;
}
};
export default userReducer;
You are overwriting state with action.user and action.users. I'm going to guess that action.user is an object and action.users is an array. So when ProfileList renders, it sees that this.state.users exists, BUT you set it to an object instead of an array.
I would make a new property in the userReducer for activeUser or make a new reducer to handle saving the active user.
const initialState = {
activeUser: null,
users: []
};
const userReducer = (state = initialState, action) => {
switch (action.type) {
case 'SET_USER':
state.activeUser = action.user;
return state;
case 'SET_USERS':
state.users = action.users
return state;
default:
return state;
}
};
export default userReducer;
Thanks Chase, you got me thinking in right direction, i've managed to find solution, the reducer was the problem indeed
i've changed the reducer to this
const userReducer = (state = {}, action) => {
switch (action.type) {
case 'SET_USER':
return { ...state, user: action.payload }
case 'SET_USERS':
return { ...state, users: action.payload }
default:
return state;
}
};
export default userReducer;
and it works:)
alose here is the action that i changed to work with newly changed reducer
// SET_USER
export const setUser = (user) => ({
type: 'SET_USER',
payload: user
})
export const startSetUser = (username) => {
return (dispatch) => {
axios.get(`${ROOT_URL}/user/${username}`)
.then(response => {
dispatch(setUser(response.data))
})
}
}
// SET_USERS ALL
export const setUsers = (users) => ({
type: 'SET_USERS',
payload: users
})
export const startSetUsers = () => {
return (dispatch) => {
axios.get(`${ROOT_URL}/users`)
.then(response => {
dispatch(setUsers(response.data))
})
}
}

Delete Item from Array (React-Native + Redux)

I have the checklist with users and when I click on the checkbox user should add to the InputField or delete from InputField, if I check to it again.
For now works only ADD.
import ...
export default class NewEvent extends React.Component {
constructor(props) {
super(props);
this.onSelect = this.onSelect.bind(this);
}
onSelect = id => {
addMembers(id) }
findSelectedContacts = (contacts, membersArray) => {
const newArr = [];
contacts.forEach(item => {
if(membersArray.indexOf(item.id.toString()) > -1) {
newArr.push(` ${item.name}`)
}
});
return newArr;
}
render() {
const { navigation, members, location, contacts } = this.props;
const membersArray = members ? members.split(',') : [];
const selectedArray = this.findSelectedContacts(contacts, membersArray)
const inputFill = selectedArray.join().trim();
return (
<InputField
customStyle={[eventStyles.input]}
icon="addGuest"
placeholder="Add guests"
onGetText={texts => {
this.handlerChangeText(texts)
}}
value={inputFill}
/>
);
}
}
Also, I have reducer, which adds guests to input:
import { handleActions } from 'redux-actions';
import * as types from '../actions/actionTypes';
export const initialState = {
members: '',
};
const addMembers = (members, id) => {
const res = members ? `${members},${id}` : `${id}`;
return res;
}
export default handleActions(
{
[types.ADD_GUEST]: (state, action) => ({
...state,
members: addMembers(state.members, action.payload),
}),
},
initialState
);
Please advise, how I can change my reducer? I need to add or delete the user from InputFiled if I click on the ONE checkbox.
Currently, it appears that you are storing the members list as a comma-separated string. A better option would be to store the list as an actual array, and then convert that to a string when it's needed in that format, e.g. rendering.
The reducer for it might look something like this (trying to follow your existing code style:
export const initialState = {
// initialState changed to array
members: [],
};
const addMember = (members, id) => {
// add id to the end of the list
return members.concat(id);
}
const removeMember = (members, id) => {
// return a new list with all values, except the matched id
return members.filter(memberId => memberId !== id);
}
export default handleActions(
{
[types.ADD_GUEST]: (state, action) => ({
...state,
members: addMember(state.members, action.payload),
}),
[types.REMOVE_GUEST]: (state, action) => ({
...state,
members: removeMember(state.members, action.payload),
}),
},
initialState
);
And if you then need the list as a string, in your component render() method - or preferrably in your react-redux mapStateToProps selector you can convert it to a string:
memberList = state.members.join(',')

Categories

Resources