I have the following code, which loads the information of the current user in the nav bar if the user is logged in or loads empty data if the user has not logged in. The code works for me, but I have a problem: when I go to any other page the nav is duplicated (specifically the rendernav function).
editprofile.js --> create the dispatch, and load the JSON
export const editProfile = (callback) => {
return function(dispatch) {
dispatch({type: 'LIST_USER_REQUEST'});
axios({
method: 'get',
url: 'https://gist.githubusercontent.com/anonymous/38c1444f753c70cf79ee980638a14de7/raw/34951eebfa006fea3db00fb492b491ac990c788e/vamos.json',
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
})
.then((response) => {
dispatch({type:'LIST_USER_SUCCESS', payload:response.data});
if (typeof callback === 'function') {
callback(null, response.data);
}
})
.catch((error) => {
dispatch({type:'LIST_USER_FAILURE'});
if (error.response.status == 401) {
browserHistory.push('login')
toastr.error(error.response.message, 'User')
}
if (typeof callback === 'function') {
callback(error.response.data, null)
}
})
}
}
EditProfileComponent.jsx -->created the component
export default class NavComponent extends Component {
render() {
return (
<table>
<thead>
<tr>
<th>SN</th>
<th>Email</th>
<th>created</th>
</tr>
</thead>
<tbody>
{this.renderSign()}
</tbody>
</table>
)
}
// --> this function duplicated, when I go to another page
renderSign() {
return this.props.allProfile.map((profile, index) => {
if (profile.status === 'SUCCESS') {
return (
<ul className="nav navbar-nav" key={index}>
<li className="dropdown user user-menu">
<a href="#" className="dropdown-toggle" data-toggle="dropdown">
<img src="img/perfil.jpg" className="user-image" alt="User Image" />
<span className="hidden-xs">{profile.user.email}</span>
</a>
</li>
</ul>
)
} else if (profile.status === 'FAIL') {
return (
<ul className="nav navbar-nav" key={index}>
<li><Link to='/sign_in'>Sign In</Link></li>
<li><Link to='/sign_up'>Sign Up</Link></li>
</ul>
)
}
}
}
Join the component with the service:
import { editProfile } from '../action/editProfile.js';
import NavComponent from '../component/editProfileComponent.jsx';
export default class EditProfileContainer extends Component {
componentDidMount() {
this.props.editProfile();
}
render () {
return (
<NavComponent allProfile={this.props.allProfile} />
);
}
}
function mapStateToProps(store) {
return {
allProfile: store.allProfile
};
}
function matchDispatchToProps(dispatch) {
return bindActionCreators(
{
editProfile:editProfile
},
dispatch
)
}
export default connect(mapStateToProps, matchDispatchToProps)(EditProfileContainer);
editProfileReducer --> the reducer
export const editProfileReducer = (state=[], action) => {
switch(action.type) {
case 'LIST_USER_REQUEST':
return state;
case 'LIST_USER_SUCCESS':
return state;
case 'LIST_USER_FAILURE':
return [...action.payload];
default:
return state;
}
}
Even though I don't see the whole code structure, it looks like you are using the wrong reducer. I see that you are using store.allProfile when you should be using store.editProfile. If the allProfile array has two items then you would have two <ul> tags when you use the .map function. If the allProfile array was intentionally intended to be used, then you should not use the .map function and instead just use just the first element of the array.
Related
Uncaught Error: When called with an action of type "DELITEM", the slice reducer for key "addItem" 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.
What is this error when I open inspection. My problem is that Onclick is not working in my app.
Here is the code:
import React from 'react'
import { useSelector } from 'react-redux'
import { useDispatch } from 'react-redux'
import { delItem } from '../redux/actions/index'
import { NavLink } from 'react-router-dom'
const Cart = () => {
const state = useSelector((state)=> state.addItem)
const dispatch = useDispatch()
const handleClose = (item) => {
dispatch(delItem(item))
}
const cartItems = (cartItem) => {
return(
<div className="px-4 my-5 bg-light rounded-3" key={cartItem.id}>
<div className="container py-4">
<button onClick={()=>handleClose(cartItem)} type="button" className="btn-close float-end" aria-label="Close"></button>
<div className="row justify-content-center">
<div className="col-md-4">
<img src={cartItem.image} alt={cartItem.title} height="200px" width="180px" />
</div>
<div className="col-md-4">
<h3>{cartItem.title}</h3>
<p className="lead fw-bold">${cartItem.price}</p>
</div>
</div>
</div>
</div>
);
}
Action:
export const addItem = (product) => {
return {
type : "ADDITEM",
payload : product
}
}
export const delItem = (product) => {
return {
type : "DELITEM",
payload : product
}
}
Reducers:
const addItem = [];
const addItems = (state = addItem, action) => {
switch (action.type) {
case "ADDITEM": return [
...state,
action.payload
]
break;
case "DELITEM": return
return state = state.filter((x)=>{
return x.id !== action.payload.id
})
break;
default: return state;
break;
}
}
export default addItems;
case "DELITEM": return
return state = state.filter((x)=>{
return x.id !== action.payload.id
})
change above code to :
make it to "DELITEM": return [...state.filter((x)=>{
return x.id !== action.payload.id
})]
I'm creating table of content Gutenberg custom block which reactive reflect h2 text.
Outside of map(), it works. but inside not working.
Please let me know how to modify this code.
thanks.
import { registerBlockType } from "#wordpress/blocks";
import { withSelect, select } from "#wordpress/data";
registerBlockType("theme/toc", {
title: "TOC",
icon: "list-view",
category: "layout",
edit: withSelect((select) => {
return {
blocks: select("core/block-editor").getBlocks(),
};
})(Edit),
save: () => {
return null;
},
});
export function Edit(props) {
const { blocks = [] } = props;
const headings = [];
blocks.forEach((el) => {
if (!(el.name === "core/heading" && el.attributes.level === 2)) {
return;
}
headings.push(el.attributes.content);
});
return (
<div>
<p>{headings[0]}</p> // this line works
<ol>
{headings.map((h2, i) => { // not working
<li key={i}>
<a>{h2}</a>
</li>;
})}
</ol>
</div>
);
}
Simply remove the curly braces so the JSXElemnt can be returned from the map function.
<ol>
{headings.map((h2, i) =>
<li key={i}>
<a>{h2}</a>
</li>;
)}
</ol>
something else to note is it's not advised to use the element index i as the key value. you may want to use a more unique value like id from the element you're looping through.
I have a component that I'll like to show if user has permission but the Can component seem to hide the component regardless of user permission.
Following is my ability.js module
import {Ability, AbilityBuilder} from "#casl/ability";
export default function defineAbilityFor(hasRole) {
const { can, cannot, build } = new AbilityBuilder(Ability)
if (hasRole !== undefined) {
console.log(hasRole)
can('manage', 'all')
can('create', 'all')
} else {
can('read', 'all')
cannot('create', 'all')
}
return build()
}
I added castle as a my main.js as follows:
const app = createApp(App)
app.use(router)
app.use(store)
app.use(abilitiesPlugin, ability(), {
useGlobalProperties: true
})
app.component(Can.name, Can)
app.mount('#app')
And the following is the component in which i need to do permission check.
<template>
<div class="d-flex flex-column px-4">
<div class="d-flex justify-content-end">
<Can I="create" an="Institution">
<button class="btn btn-sm btn-outline-success" #click="addInst = true">{{ action }}</button>
</Can>
</div>
<ListInstitutions v-if="addInst === false"/>
<Can I="create" an="Institution">
<AddInstitution v-if="addInst === true" #created="closeModal"/>
</Can>
</div>
</template>
<script>
import AddInstitution from "#/components/institution/AddInstitution";
import ListInstitutions from "#/components/institution/ListInstitutions";
import { Can } from "#casl/vue";
export default {
name: 'InstitutionPage',
components: {AddInstitution, ListInstitutions, Can},
data() {
return {
addInst: false,
action: this.addInst ? 'Institutions' : 'Add Institution'
}
},
methods: {
closeModal() {
this.addInst = false
}
}
}
</script>
Thank you so much #Sergii Stotskyi.
I eventually defined ability in a definedAbility.js file as follows:
import { AbilityBuilder, Ability } from "#casl/ability";
export default function defineAbilityFor(userRole) {
const { can, cannot, build } = new AbilityBuilder(Ability);
if (userRole === 'user') {
can ('read', 'Institution')
cannot(['create', 'update'], 'all')
}
if (userRole === 'reg_admin_stroke' || userRole === 'admin') {
can('manage', 'all')
}
return build()
}
And then in every component where ability is required, inside the setup() method I create the ability as follows:
...
import definedAbility from '#/services/definedAbility.js'
...
const ability = definedAbility(userRole.value)
return {
ability
}
And In my template did something like:
<div v-if="ability.can('create', 'Institution')">
<button class="btn btn-sm btn-outline-success" #click="addInst = true">{{ action }}</button>
</div>
All:
I am pretty new to redux and react-redux, I wonder if I design a todo item component(the element unit of a todolist) like
const todo => ({id, todocontent}) {
return <li key={id}>{todocontent}</li>
}
then how can I build that mapStateToProp function and use it with connect?
Say the todo list data is like :
{
title: "TodoList",
version:"1.0",
todolist: [
{id:"id1", todocontent: "todo 1"},
{id:"id2", todocontent: "todo 2"}
]
}
Something like this:
class YourComponent extends Component {
render () {
return (
<div>
{this.props.items.forEach((item) => {
return <todo id={item.id} todocontent={item.todocontent} />
})}
</div>
)
}
}
const mapStateToProps = (state) => {
return {
items: state.<yourReducerName>.todolist
}
}
connect(mapStateToProps)(YourComponent)
I am playing with the Meteor Apollo demo repo.
I am having difficulty passing variables down to children with React. I am getting an error
imports/ui/Container.jsx:10:6: Unexpected token (10:6)
The below code is the Container.jsx component:
import React from 'react';
import { Accounts } from 'meteor/std:accounts-ui';
class Container extends React.Component {
render() {
let userId = this.props.userId;
let currentUser = this.props.currentUser;
}
return (
<Accounts.ui.LoginForm />
{ userId ? (
<div>
<pre>{JSON.stringify(currentUser, null, 2)}</pre>
<button onClick={() => currentUser.refetch()}>Refetch!</button>
</div>
) : 'Please log in!' }
);
}
}
It is passed props via the Meteor Apollo data system (I have omitted some imports at the top):
const App = ({ userId, currentUser }) => {
return (
<div>
<Sidebar />
<Header />
<Container userId={userId} currentUser={currentUser} />
</div>
)
}
// This container brings in Apollo GraphQL data
const AppWithData = connect({
mapQueriesToProps({ ownProps }) {
if (ownProps.userId) {
return {
currentUser: {
query: `
query getUserData ($id: String!) {
user(id: $id) {
emails {
address
verified
}
randomString
}
}
`,
variables: {
id: ownProps.userId,
},
},
};
}
},
})(App);
// This container brings in Tracker-enabled Meteor data
const AppWithUserId = createContainer(() => {
return {
userId: Meteor.userId(),
};
}, AppWithData);
export default AppWithUserId;
I would really appreciate some pointers.
I believe the error is that you accidentally ended the render function before the return statement.
render() { // <- here it starts
let userId = this.props.userId;
let currentUser = this.props.currentUser;
} // <- here it ends
Another error is that your return statement doesn't return a single DOM element, but two of them: an Accounts.ui.LoginForm and a div. The return function should only return one element. Just put the entire thing into a single <div>.