I'm loading it like that:
const store = createStore(
AppReducer,
composeEnhancers(applyMiddleware(createDebounce(), thunkMiddleware,
websocketMiddleware)),
);
I've got the following action:
export const toggleCardLayersMenu = (index, value) => ({
type: "CARD:TOGGLE_LAYERS_MENU",
index: index,
value: value,
});
Which I dispatch something to open a menu and something to close it.
Now, the chrome extension display the action with a small delay if the action is used to open the menu, and won't display the action at all if it's used to close the menu.
It occurs only with that specific action. What are the possible causes, and what can be done?
Edit:
This is where I dispatch the action:
...
<IconButton
onClick={ e => dispatches.toggleCardLayersMenu(e.currentTarget) }
><LayersIcon />
</IconButton>
<Menu
anchorEl={ card.menuBtnElem }
open={ card.isMenuOpen }
onRequestClose={ () => dispatches.toggleCardLayersMenu(null) }
style={ styles.menu }
>
...
This is the dispatchToProps():
const mapDispatchToProps = (dispatch, ownProps) => ({ dispatches: {
updateCardLayers(value) {
dispatch(displayActions.updateCardLayers(ownProps.index, value));
},
toggleCardLayersMenu(value) {
dispatch(displayActions.toggleCardLayersMenu(ownProps.index, value));
},
...
Related
I am using react redux-thunk. I have a set of users data that I get from an API and this is the schema:
.
I've connected the "active" property with the checked attribute of a Switch MUI button, so naturally when calling the API I have some users with their switch button already on "true". What I am trying to do is to just make the switch functional, and just be able to click it and change its state, not necessarily doing anything with that.
Here's my toggleType.js:
export const TOGGLE = "TOGGLE";
Here's my toggleAction.js:
import { TOGGLE } from "./toggleType";
const statusToggleAction = () => {
return {
type: TOGGLE,
};
};
export const statusToggle = () => {
return (dispatch) => {
dispatch(statusToggleAction);
};
};
Here's my toggleReducer.js:
import { TOGGLE } from "./toggleType";
const initialState = {
status: false,
};
const toggleReducer = (state = initialState, action) => {
switch (action.type) {
case TOGGLE:
status: true;
default:
return state;
}
};
export default toggleReducer;
Everything is under my userContainer.js, like that:
function UserContainer({ userData, fetchUsers }) {
useEffect(() => {
fetchUsers();
}, []);
return userData.loading ? (
<h2>Loading</h2>
) : userData.error ? (
<h2>{userData.error}</h2>
) : (
<Container maxWidth="lg" style={{ flexGrow: 1, height: "100%" }}>
<h2>User List</h2>
<div>
{userData &&
userData.users &&
userData.users.map((user) => (
<div key={user.id}>
<p>{user.name}</p>
<Switch checked={user.active} onChange={statusToggle()} />
</div>
))}
</div>
</Container>
);
}
const mapStateToProps = (state) => {
return { userData: state.user, statusToggle: state.status };
};
const mapDispatchToProps = (dispatch) => {
return {
fetchUsers: () => dispatch(fetchUsers()),
statusToggle: () => dispatch(statusToggle()),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(UserContainer);
This is the error I am getting whenever I am clicking one of those switches:
Any ideas are welcome, I "learned" redux like 3 days ago!
toggleReducer function in toggleReducer.js, replace status: true; with return { status: true }.
Just return action in statusToggle function in toggleAction.js without dispatch as following.
export const statusToggle = () => {
return statusToggleAction();
};
Or just call statusToggleAction directly in userContainer.js as following.
export const statusToggle = () => {
return (dispatch) => {
dispatch(statusToggleAction());
};
};
I'm trying to change the zoom level by passing a level of 13 into my action creator.
But get this error:
Error: Given action "ZOOM_SELECTED", reducer "zoom" returned undefined. To ignore an action, you must explicitly return the previous state. If you want this reducer to hold no value, you can return null instead of undefined.
Do you know how to change the current state passing a new zoom level into my action creator?
render() {
console.log(this.props.zoom)
return (
<div>
<Map className='map' center={this.props.mapCenter} zoom={this.props.zoom}>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<Marker position={this.props.mapCenter}>
<Popup>
A pretty CSS3 popup. <br /> Easily customizable.
</Popup>
</Marker>
</Map> <button onClick={() => this.props.selectZoom((13))} >Ă„ndra zoom</button>
</div >
)
}
};
const mapStateToProps = (state) => {
return {
zoom: state.zoom,
mapCenter: state.mapCenterPosition
}
};
const mapDispatchToProps = dispatch => {
return {
selectZoom: () => dispatch(selectZoom())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(MapComponent); ```
Action Creator
export const selectZoom = (zoom) => {
console.log('action',zoom)
return {
type: 'ZOOM_SELECTED',
payload: zoom
};
};
Reducer
const zoomReducer = (state = 8, action) => {
if(action.type === 'ZOOM_SELECTED') {
return action.payload;
} else {
return state;
}
};
The problem is in the mapDispatchToProps - you are not passing the zoom to the action in the callback. You need to do this:
const mapDispatchToProps = dispatch => {
return {
selectZoom: (zoom) => dispatch(selectZoom(zoom))
}
}
Without it, no argument gets passed and the payload has value of undefined. Since you are returning payload directly in the reducer, it appears as if it is not returning anything.
Any input on how I can fix this...
My goal here is for the user to search 'Jane Smith' into the search bar, click the search button and display the cards that contain 'Jane Smith'
Currently with what I have, the user searches for 'Jane Smith' which is saved as the 'term' and once the user clicks the search button the 'fetchStudents' function fails saying it doesn't have 'term'
I'm having trouble passing a value of 'term' from SearchBarInput tag over to the SearchBarButton tag, so in the student container I can use it in the mapDispatchToProps function.
My searchBar component consists of
const SearchBar = ({ onClick, value }) => (
<SearchBarWrapper>
<div>
<FontAwesomeIcon icon="search" className="searchIcon" />
<SearchBarInput
name="searchBarInput"
placeholder=" Search for something..."
label="searchBar"
defaultValue={value}
/>
<SearchBarButton onClick={onClick}>Search</SearchBarButton>
</div>
</SearchBarWrapper>
);
export default SearchBar;
In my student container I have
const Students = ({ studentsPayload = [], onClick }) => (
<StudentsContainer>
<SearchBarContainer>
<SearchBar onClick={onClick} />
</SearchBarContainer>
{studentsPayload.length > 0 ? (
<StudentsCard data={studentsPayload} />
) : null}
</StudentsContainer>
);
const mapDispatchToProps = dispatch => ({ onClick: ({ target: { value } }) => dispatch(fetchStudents(value)) });
const mapStateToProps = ({ students: { studentsPayload } }) => ({ studentsPayload });
/**
* Connects component to redux store
*/
const enhancer = connect(
mapStateToProps,
mapDispatchToProps,
)(StudentSearch);
export default enhancer;
My fetchStudents in actions looks like this
export const fetchStudents = term => async dispatch => {
try {
const { data: { items } } = await fetchData(
`${process.env.REACT_APP_STUDENTS_URLP1}${term}${process.env.REACT_APP_STUDENTS_URLP2}`);
dispatch({ type: FETCH_STUDENTS, studentsPayload: items });
} catch (error) {
throw new Error(error);
}
};
Thanks in advance!
You could convert the SearchBar component to utilize the useState hook. Then you can pass an onChange handler to your SearchInput that takes in the event and updates the SearchBar state based off of the event.target.value. Then when you hit submit, you can pass in the input value that you have stored in state.
Thank you for the help! I solved the problem by making the following changes :)
In the Search bar component
const SearchBar = ({ onClickButton, value, onValueChange }) => (
<SearchBarWrapper>
<div>
<FontAwesomeIcon icon="search" className="searchIcon" />
<SearchBarInput
name="searchBarInput"
label="searchBar"
placeholder="Search for something"
value={value}
onChange={onValueChange}
/>
<SearchBarButton onClick={() => onClickButton(value)}>Search</SearchBarButton>
</div>
</SearchBarWrapper>
);
export default SearchBar;
In the Student container
const Students = ({ studentsPayload = [], onClickButton }) => {
const [value, setValue] = useState('');
const handleChange = ({ target: { value } }) => setValue(value);
return (
<StudentsContainer>
<SearchBarContainer>
<SearchBar
onClickButton={onClickButton}
onValueChange={handleChange}
value={value}
/>
</SearchBarContainer>
{studentsPayload.length > 0 ? (
<StudentsCard data={studentsPayload} />
) : null}
</StudentsContainer>
);
};
const mapDispatchToProps = dispatch => ({ onClickButton: value => dispatch(fetchStudents(value)) });
const mapStateToProps = ({ students: { studentsPayload } }) => ({ studentsPayload });
/**
* Connects component to redux store
*/
const enhancer = connect(
mapStateToProps,
mapDispatchToProps,
)(Students);
export default enhancer;
I am making a react app, I have a "categories" collection and a presentation layer with a button to remove a category. My redux state refresh if I reload the page otherwise is persistent. I don't understand why it is not working since seems immutable to me, plus I am using the same pattern in another component and works fine. What am I missing ?
Here the reducer:
import {DELETE_CATEGORY} from './../../actionType';
const initialState = {
singleCategory: {},
categories: []
}
export default function(state = initialState, action){
switch(action.type){
case DELETE_CATEGORY:
return {
...state,
categories: state.categories.filter (category=>category._id !== category.payload)
}
default:
return state;
}
}
By the way if I refresh the page the store is filtered correctly.
Here is my action:
// Delete Category by id
export const deleteCategory = (categoryId) => dispatch => {
console.log('disp');
axios
.delete(`/api/categories/${categoryId}`)
.then( success => {
dispatch(successMessage);
return dispatch({
type: DELETE_CATEGORY,
payload: categoryId
})
})
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
}),
);
};
Here is my component:
class ShowCategoriesPage extends Component {
componentDidMount(){
this.props.getCategories()
}
handleDeleteCategory = (categoryId) => {
this.props.deleteCategory(categoryId);
}
render() {
const { categories } = this.props.categories;
return (
<SidebarLayout>
{categories.map(category => (
<CategoryCard
name={category.name}
type={category.type}
id={category._id}
handleDeleteCategory={this.handleDeleteCategory}
/>
))}
<Button variant="contained" color="primary" style={{marginTop: '36px'}} >
ADD NEW CATEGORY
</Button>
</SidebarLayout>
)
}
}
and this is the component:
const SimpleCard = ({ classes, name, type, id, deleteCategory })=>{
const onDeleteCategory = (categoryId) => {
deleteCategory(categoryId);
}
return (
<Card className={classes.card}>
<CardContent>
<Typography variant="h5" component="h2">
{name}
</Typography>
<Typography className={classes.pos} color="textSecondary">
{type}
</Typography>
<Button variant="small" color="primary" style={{marginTop: '36px'}} onClick={()=>onDeleteCategory(id)}>
REMOVE
</Button>
<Button variant="small" color="primary" style={{marginTop: '36px'}} >
EDIT
</Button>
</CardContent>
</Card>
);
}
I read the official redux documentation and it advise to use splice or filter like I did, can you understand why redux state is not refreshing clicking on delete button?
I want to avoid to force refresh the state.
are you sure this line is correct
categories: state.categories.filter (category=>category._id !== category.payload)
and not
categories: state.categories.filter (category=>category._id !== action.payload)
I had designed the action and reducer in below:
// Action Types
const PUSH_BREADCRUMB = 'PUSH_BREADCRUMB';
const POP_BREADCRUMB = 'POP_BREADCRUMB';
// ActionCreator
const pushBreadcrumb = (payload: { text, link }) => ({
type: PUSH_BREADCRUMB,
payload
});
const popBreadcrumb = () => ({ type: PUSH_BREADCRUMB });
// Reducer
const initState = [
{ text: 'Home', link: '/' }
];
const breadcumbsReducer = (state = initState, action) => {
switch (action.type) {
case PUSH_BREADCRUMB:
return [...state, action.payload];
case POP_BREADCRUMB:
return state.slice(0, state.length - 1);
default:
return state
};
};
Below is my component and router:
//...import some components
// ...connect and map breadcrumbs
const Header = ({ breadcrumbs }) => {
return (
<ul>
{breadcrumbs.map({ item, link } =>
<li key={link}><Link to={link}>{text}</Link></li>
)}
</ul>
);
};
ReactDOM.render(
<Provider>
<Router>
<AppContainer>
<Header />
<Route exact path='/orders' component={OrderListContainer} />
<Route path='/orders/:id' component={OrderDetailContainer} />
</AppContainer>
</Router>
</Provider>,
document.getElementById('root')
);
Then:
Dispatch pushBreadcrumb in OrderListContainer#componenetWillMount and OrderDetailContainer#componenetWillMount
Dispatch popBreadcrumb in OrderListContainer#componentWillUnmount and OrderDetailContainer#componentWillUnmount.
The excepted breadcrumbs is Home > Orders > Detail;
But when i stay in /orders/:id and refresh page, it will be Home > Detail.
Is there better way to implement breadcrumbs with redux?
That happens because state is reset after you refresh. You can try to implement your solution with persistent store - this will keep selected state keys to browser localStorage.