ESlinter asking on useEffect a dep - use-effect

I have this useEffect:
useEffect(() => {
if (allValidated) {
setState({
...state,
buttonDisabled: false
});
} else {
setState({
...state,
buttonDisabled: true
});
}
}, [allValidated, validation]); // HERE is the warning:
React Hook useEffect has a missing dependency: 'state'. Either include it or remove the dependency array. You can also do a functional update 'setState(s => ...)' if you only need 'state' in the 'setState' call
But if I add state to the deps arrays the Component enters in a loop.
state is defined as:
const [state, setState] = useState(defaultState);
Where defaultState is this object, and can be updated in different parts:
const defaultState = {
amplifyError: false,
buttonDisabled: true,
errors: defaultError,
password: '',
show: false,
username: '',
validation: defaultValidation
};
I don't want to use a #ts-ignore because I think that it could be resolved, and not ignored. Any hint?

As said in the warning, you can also do a functional update setState(s => ...). For example, your first setState would look like this:
setState(s => {
...s,
buttonDisabled: false
});
and so forth for the second one !

Related

How to use data from DB as initial state with Zustand

I'm using the T3 stack (TypeScript, tRPC, Prisma, Next, etc) and I want to either load data from my database using a tRPC query, or use an empty array in my Zustand store. I keep getting an error saying:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app See https://reactjs.org/link/invalid-hook-call for tips about how to debug
and fix this problem. error - TypeError: Cannot read properties of
null (reading 'useContext')
(node:internal/process/task_queues:96:5) { page: '/' }
here's the code generating the error:
type Image = {
url: string;
prompt: string;
};
interface UserState {
images: Image[] | [];
isLoading: boolean;
addImage: (url: string, prompt: string) => void;
removeImage: (url: string, prompt: string) => void;
setLoading: () => void;
}
export const useStore = create<UserState>((set) => {
const { data: sessionData } = useSession();
const dbImages = trpc.images.list.useQuery({
limit: 20,
userId: sessionData?.user?.id ?? "",
}).data?.items;
return {
// initial state
images: dbImages ? dbImages : [],
isLoading: false,
// methods for manipulating state
addImage: (url, prompt) => {
set((state) => ({
images: [
...state.images,
{
url: url,
prompt: prompt,
} as Image,
],
}));
},
removeImage: (url: string) => {
set((state) => ({
images: state.images?.filter((x) => x.url !== url),
}));
},
setLoading: () => {
set((state) => ({
isLoading: !state.isLoading,
}));
},
};
});
What am I doing wrong here? I'm still in the learning phases and would appreciate best practices, etc.

State is changing but transitions are not triggered in Xstate

I am working with xstate with Nextjs. Now I am stuck somewhere.
import { assign, createMachine, interpret } from "xstate";
export interface toggleAuth {
isAuthenticated: boolean;
user: {
name: string | undefined;
};
}
// console.log(getCachedData());
export const authMachine = createMachine<toggleAuth>({
id: "auth",
initial: "unauthenticated",
context: {
isAuthenticated: false,
user: {
name: undefined,
},
},
states: {
authenticated: {
on: {
toggle: {
target: "unauthenticated",
},
},
entry: assign({
user: (ctx) => (ctx.user = { name: "Pranta" }),
isAuthenticated: (ctx) => (ctx.isAuthenticated = true),
}),
},
unauthenticated: {
on: {
toggle: {
target: "authenticated",
},
},
entry: assign({
user: (ctx) => (ctx.user = { name: undefined }),
isAuthenticated: (ctx) => (ctx.isAuthenticated = false),
}),
},
},
});
const service = interpret(authMachine);
service.onTransition((state) => console.log(state));
So I was watching the docs. According to them, whenever I transition from unauthenticated to authenticated and authenticated to unauthenticated, it should console log it for me. But it doesn't. It does only one time. What's happening here. Also, is it okay to define my machine like this? Thanks in advance.
It's not logging because you're not changing state; no event is ever being sent.
Please re-read the documentation on assigning to context - you are mutating context instead of assigning new values; the assigners should always be pure.
If you want to see the state change, you need to send a toggle event in this case:
service.send('toggle');
Also, there is no need for isAuthenticated; this is redundant, since that state is represented by the finite state (state.value) of your machine.

react js request result

Hello I have a problem getting a value and setting in my state in react
I can see data in console of my api response and everything goes well.
export default class index extends Component {
constructor(props){
super(props)
this.state={ products: [], filteredProducts:[]}
}
componentDidMount(){
api.get('/products').then( result => this.setState({
products: result.data.listProducts,
filteredProducts: result.data.listProducts
}))
console.log(this.state.products)
}
but when I console my state value, it appears an empty array
index.js:16 [] console.log(this.state
index.js:11 (5) [{…}, {…}, {…}, {…}, {…}] console.log( data request
Well I don't know if it's a problem with my back end
I made a map to filter what I will return to my front end since I have
an array of 3 objects
I don't know if I made the best option or if I can do better, if I can improve the code I would be happy if someone could alert me:
async getAllProduct(req,res){
try {
const results = await Products.findAll({
// raw: true, <= remove
attributes:['id','name', 'float', 'price'],
include: [{
model: SubCategory,
as: 'subcategory',
attributes: ['id','name'],
},
{
model:Exteriors,
as: 'exteriors',
attributes: ['id','name']
},
{
model:Types,
as: 'types',
attributes: ['id','name']
},
],
})
const listProducts = []
results.map(record =>
record.get({ plain: true }));
results.map( (products) => {
const model = {
id: products.id,
name: products.name,
float: products.float,
price: products.price,
id_sub: products.subcategory.id,
subcategory: products.subcategory.name,
id_types: products.types.id,
type: products.types.name,
id_ext: products.exteriors.id,
exterior: products.exteriors.name,
}
listProducts.push(model);
})
if(listProducts){return res.status(200).json({listProducts})}
else{return res.status(400).json({result: 'failed to get Products'})}
} catch (error) {
console.error(error);
}
}
setState is async, you can't see updated state right after setting the state,
You can have callback in setState to check the updated state,
this.setState({
products: result.data.listProducts,
filteredProducts: result.data.listProducts
}, () => console.log(this.state.products)) //callback method
If you console.log right after a state update you will log the old state. Try logging the state in componentDidUpdate to see if the state is actually empty:
componentDidUpdate() {
console.log(this.state)
}

Formik, jest, yup : how to test validation?

i can't find a way to test form yup validation:
it('displays error on submit if name is empty', async () => {
const wrapper = mount(<MyFormik/>)
const getForm = () => wrapper.find('form')
wrapper.find('input[name="name"]').simulate('change', {
persist: () => {},
target: {
name: 'name',
value: ''
}
})
wrapper
.find('MyInnerForm')
.props()
.submitForm()
await wait(0) // await next tick or even 1s...
wrapper.update()
expect(
wrapper
.update()
.find('.error')
.exists()
)
.toBeTruthy() // FALSE!
})
No matter if i wait after submit, update wrapper errors prop is always empty.
And the solution here are not working for me:
https://github.com/jaredpalmer/formik/issues/1146
https://github.com/jaredpalmer/formik/issues/110
Looks like wrapper won't update
Here's the log of formik props after submit:
{ errors: {},
label: '',
name: 'name',
type: 'text',
values: { name: '' },
touched: { name: true },
isValidating: false,
status: undefined,
initialValues: { name: '' },
validateOnChange: true,
validateOnBlur: true } }
...
submitCount: 1,
isValid: false,
You can validate the form values directly on your validation schema.
const yup = require('yup')
const contactSchema = yup.object({
name: yup.string()
.required(),
age: yup.number()
.required()
.positive()
.integer()
})
const errors = await contactSchema.validate({
name: 'Kenneth',
age: -35.5
}).catch(function(err) {
return err
});
console.log("errors", errors);
https://runkit.com/kluplau/5defa8cd122cf6001a3034c7
Without seeing your component I'm not entirely sure what's going wrong. This is likely not to be working:
wrapper
.find('MyInnerForm')
.props()
.submitForm()
If your component MyInnerForm contains a Formik form calling submitForm() there will not cause Formik's validation to run. I would instead do something like this:
wrapper.find("form").simulate("submit");
However if that isn't solving your issue I made a full example that you can have a look at here.

Mongodb/mongoose omit a field in response [duplicate]

I have a NodeJS application with Mongoose ODM(Mongoose 3.3.1). I want to retrieve all fields except 1 from my collection.For Example: I have a collection Product Which have 6 fields,I want to select all except a field "Image" . I used "exclude" method, but got error..
This was my code.
var Query = models.Product.find();
Query.exclude('title Image');
if (req.params.id) {
Query.where('_id', req.params.id);
}
Query.exec(function (err, product) {
if (!err) {
return res.send({ 'statusCode': 200, 'statusText': 'OK', 'data': product });
} else {
return res.send(500);
}
});
But this returns error
Express
500 TypeError: Object #<Query> has no method 'exclude'.........
Also I tried, var Query = models.Product.find().exclude('title','Image'); and var Query = models.Product.find({}).exclude('title','Image'); But getting the same error. How to exclude one/(two) particular fields from a collection in Mongoose.
Use query.select for field selection in the current (3.x) Mongoose builds.
Prefix a field name you want to exclude with a -; so in your case:
Query.select('-Image');
Quick aside: in JavaScript, variables starting with a capital letter should be reserved for constructor functions. So consider renaming Query as query in your code.
I don't know where you read about that .exclude function, because I can't find it in any documentation.
But you can exclude fields by using the second parameter of the find method.
Here is an example from the official documentation:
db.inventory.find( { type: 'food' }, { type:0 } )
This operation returns all documents where the value of the type field is food, but does not include the type field in the output.
Model.findOne({ _id: Your Id}, { password: 0, name: 0 }, function(err, user){
// put your code
});
this code worked in my project. Thanks!! have a nice day.
You could do this
const products = await Product.find().select(['-image'])
I am use this with async await
async (req, res) => {
try {
await User.findById(req.user,'name email',(err, user) => {
if(err || !user){
return res.status(404)
} else {
return res.status(200).json({
user,
});
}
});
} catch (error) {
console.log(error);
}
In the updated version of Mongoose you can use it in this way as below to get selected fields.
user.findById({_id: req.body.id}, 'username phno address').then(response => {
res.status(200).json({
result: true,
details: response
});
}).catch(err => {
res.status(500).json({ result: false });
});
I'm working on a feature. I store a userId array name "collectedUser" than who is collected the project. And I just want to return a field "isCollected" instead of "collectedUsers". So select is not what I want. But I got this solution.
This is after I get projects from database, I add "isCollected".
for (const item of projects) {
item.set("isCollected", item.collectedUsers.includes(userId), {
strict: false,
})
}
And this is in Decorator #Schema
#Schema({
timestamps: true,
toObject: {
virtuals: true,
versionKey: false,
transform: (doc, ret, options): Partial<Project> => {
return {
...ret,
projectManagers: undefined,
projectMembers: undefined,
collectedUsers: undefined
}
}
}
})
Finally in my controller
projects = projects.map(i => i.toObject())
It's a strange tricks that set undefined, but it really work.
Btw I'm using nestjs.
You can do it like this
const products = await Product.find().select({
"image": 0
});
For anyone looking for a way to always omit a field - more like a global option rather than doing so in the query e.g. a password field, using a getter that returns undefined also works
{
password: {
type: String,
required: true,
get: () => undefined,
},
}
NB: Getters must be enabled with option { toObject: { getters:true } }
you can exclude the field from the schema definition
by adding the attribute
excludedField : {
...
select: false,
...
}
whenever you want to add it to your result,
add this to your find()
find().select('+excludedFiled')

Resources