I try to set state , but I got error "Hooks can only be called inside of the body of a function component."
This is the code import React, { useState } from "react";
import AuthService from "../Services/AuthService";
const [Searchfile, setSearchfile] = useState([]);
AuthService.getalldata().then((res) => {
setSearchfile(res.data);
});
// const Searchfile = [
// { title: "ayam", singer: "gigi" },
// { title: "ucuk", singer: "asd" },
// ];
export default Searchfile;
Can someone explain to me why that is error and how to fix it ? :D
try adding a functional component and paste the code there.
import React, { useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
return (
<div>
<p> {count}</p>
<button onClick={() => setCount(count + 1)}>
click here
</button>
</div>
);
}
You are breaking the rules of hooks by attempting to call the useState hook outside of a functional component. After looking over your code, it seems like you want to create a custom hook of some sort so my suggestion would be todo something like this:
import { useState } from 'react'
import AuthService from "../Services/AuthService";
const UseSearchFile = () => {
const [Searchfile, setSearchfile] = useState([]);
AuthService.getalldata().then((res) => {
setSearchfile(res.data);
});
return {Searchfile}
}
export default UseSearchFile
you would then import it where you need it like so:
import UseSearchFile from ...correct path
and call it like so:
const { Searchfile } = UseSearchFile()
Related
I have an object: dynamicJSON that is changing. I would like to pass this object down to multiple dependencies: componentA, componentB. I also want the parts of the dependencies using the object to render when the object is changed.
I tried the useContext Hook, but received a dependency cycle error. What is the proper way to pass reactive values down to dependencies in react?
App.js
import { componetA } from "compA"
import { componetB } from "compB"
import { fetchLatestValue} from "api/fetchLatestValue"
import { useEffect } from "react"
export default function App() {
const dynamicJSON = ???;
useEffect(() => {
let timeoutId;
async function getlatestValue() {
try {
const data = await fetchLatestValue();
// update dynamicJSON here.
} catch (error) {
}
timeoutId = setTimeout(getlatestValue, 1000 * 1);
}
getlatestValue();
return () => {
clearTimeout(timeoutId);
};
}, []);
return (
<componetA />
<componetB />
);
}
compA
export default function componentA() {
const dynamicJSON = ???;
return(
<div>
{dynamicJSON.value}
</div>
)
};
Have you tried useEffect() with a dependency array? If anything in the dependency array is changed, the hook function will be triggered.
Reference: https://reactjs.org/docs/hooks-reference.html#conditionally-firing-an-effect
Sorry I mis-read your question, you should pass dynamicJSON into both components as a prop. Make dynamicJSON a state is also a good idea.
Rule of thumb: if a prop or state of a component is changed, then this component is rerendered.
import { ComponentA } from "compA";
import { ComponentB } from "compB";
import { useEffect, useState } from "react";
export default function App() {
const [dynamicJSON, setDynamicJSON] = useState({});
//...omit
return (
<ComponentA dynamicJSON={dynamicJSON}/>
<ComponentB dynamicJSON={dynamicJSON}/>
);
}
CompA.js
export default function ComponentA(props) {
const { dynamicJSON } = props;
return(
<div>
{dynamicJSON.value}
</div>
)
};
I have a problem with hooks in ReactJS
as you see here i defined a prop that should call from child component
but when i want to change the value by calling change component it doesn't work and my state doesn't set.
can someone help me?
don't forget to read the comments
import React, {useState} from "react";
import Collection from "./Collection";
import ReminderPeriod from "./ReminderPeriod";
function SingleReminderPage() {
const [collection, setCollection] = useState(null);
const setSelectedCollection = (e) => {
setCollection(e);
console.log(e); // returns the true value
console.log(collection); // returns null
}
return(
<div>
<Collection onChoosed={(e) => setSelectedCollection(e)}/>
</div>
)
}
export default SingleReminderPage;
Use setState with a callback function
const setSelectedCollection = (e) => {
setCollection((state)=> {...state, e});
}
setCollection(e) - wont update the state immediately.
I want to Understand SetState and Prevstate in ReactJS
This might help you around, the useEffect will be called on each colletion update
import React, { useState, useEffect } from "react";
import Collection from "./Collection";
import ReminderPeriod from "./ReminderPeriod";
function SingleReminderPage() {
const [collection, setCollection] = useState(null);
useEffect(() => {
console.log(collection)
}, [collection])
return (
<div>
<Collection onChoosed={(e) => setCollection(e)} />
</div>
)
}
export default SingleReminderPage;
it seems like the setCollection is called after the logging action to check something like that you can print the collection value on the component itself
import React, {useState} from "react";
import Collection from "./Collection";
import ReminderPeriod from "./ReminderPeriod";
function SingleReminderPage() {
const [collection, setCollection] = useState(null);
const setSelectedCollection = (e) => {
setCollection(e);
console.log(e); // returns the true value
console.log(collection); // returns null
}
return(
<div>
{collection}
<Collection onChoosed={(e) => setSelectedCollection(e)}/>
</div>
)
}
export default SingleReminderPage;
I'm trying to use localForage in a reactjs app. Calling setItem and then getItem works exactly once. The value that was set with setItem is correctly recovered by getItem. If they are called again after that, a TypeError is thrown:
TypeError: self is undefined localforage.js:1012
The error can be reproduced with a minimal reactjs app, here's just a single functional component.
When the component is rendered, it calls setItem and getItem.
It rerenders on a button click and calls setItem and getItem again. This causes the TypeError.
import React, { useState } from "react";
import { getItem, setItem } from "localforage";
function App() {
const [state, setState] = useState(0);
setItem("test", state).then(() => {
console.log("used localForage");
});
getItem("test").then((val) => {
console.log("got: ", val);
});
return (
<button
onClick={() => {
setState(Math.floor(Math.random() * 10));
}}
>
{state}
</button>
);
}
export default App;
Here is an online version on codesandbox.
Somehow it loses the context internally when importing methods separately, works fine when importing localforage as a whole
import React, { useState } from "react";
import localforage from "localforage";
function App() {
const [state, setState] = useState(0);
localforage.setItem("test", state).then(() => {
console.log("used localForage");
});
localforage.getItem("test").then(val => {
console.log("got: ", val);
});
return (
<button
onClick={() => {
setState(Math.floor(Math.random() * 10));
}}
>
{state}
</button>
);
}
export default App;
It's really basic I guess. I'm trying to add onClick callback to my script & I believe I'm missing a value that would be responsible for finding the actual item.
Main script
import React from 'react';
import { CSVLink } from 'react-csv';
import { data } from 'constants/data';
import GetAppIcon from '#material-ui/icons/GetApp';
import PropTypes from 'prop-types';
const handleClick = (callback) => {
callback(callback);
};
const DownloadData = (props) => {
const { callback } = props;
return (
<>
<CSVLink
data={data}
onClick={() => handleClick(callback)}
>
<GetAppIcon />
</CSVLink>
</>
);
};
DownloadData.propTypes = {
callback: PropTypes.func.isRequired,
};
export default DownloadData;
Storybook code
import React from 'react';
import DownloadData from 'common/components/DownloadData';
import { data } from 'constants/data';
import { action } from '#storybook/addon-actions';
export default {
title: 'DownloadData',
component: DownloadData,
};
export const download = () => (
<DownloadData
data={data}
callback={action('icon-clicked')}
/>
);
So right now with this code on click in the storybook I'd get null and I'm looking for an object.
One of the potential issues I can see is that your handleClick function is stored as it is in-memory, when you import the component. That means you're keeping reference of something that doesn't exists and expects to use it when rendering the component with the callback prop.
Each instance of a component should have its own function. To fix it, move the function declaration inside the component. Like this:
const Foo = ({ callback }) => {
// handleClick needs to be inside here
const handleClick = callback => {
console.log("clicked");
callback(callback);
};
return <div onClick={() => handleClick(callback)}>Click me!</div>;
};
Check this example.
If this doesn't fix your problem, then there is something wrong with how you're implementing Storybook. Like a missing context.
I have a project which was created using create react app.
Lets say I have a simple component like this -
import React from 'react';
import React, { useState } from "react";
function Example() {
const [data, setData] = useState(0);
const onClickHandler = () => {
setData(data + 1);
};
return (
<div className="button" onClick={onClickHandler}>
{data}
</div>
);
}
export default Example;
I will test the component like this -
import React from "react";
import { shallow } from "enzyme";
import Example from "./Example";
it("example test", () => {
const wrraper = shallow(<Example />);
wrraper.find(".button").simulate("click");
expect("test somethig");
});
If I will use styles.module like this -
import React, { useState } from "react";
import styles from "./styles.module.scss";
function Example() {
const [data, setData] = useState(0);
const onClickHandler = () => {
setData(data + 1);
};
return (
<div className={styles.button} onClick={onClickHandler}>
{data}
</div>
);
}
export default Example;
I will not be able to find and element in the test using ".button" anymore, since webpack will add a hash to my class name when I am using css modules.
So how can I test react compoennt while using css modules? Only by adding an Id to the element?
It fills wrong to change my code so I will be able to test it.
There are many alternative selectors, which you can find here. That said, when working with (s)css modules, I tend to lean on the element position within the component:
wrapper.find("div").first() will select the first div element within the component heirarchy (or in your example, it'll select the div with the "styles.button" className).
Another alternative is to use template literals. With the example below, I essentially created an escape hatch to select .some-classname:
import React, { useCallback, useState } from "react";
import { button } from "./styles.module.scss";
function Example() {
const [data, setData] = useState(0);
const onClickHandler = useCallback(() => {
setData(prevSate => prevState + 1);
}, [setData]);
return (
<div className={`${button} some-classname`} onClick={onClickHandler}>
{data}
</div>
);
}
export default Example;
And lastly, you can use a data-attribute -- like data-test-id (which is becoming more popular because of react-testing-library) to create a simple static selector that can be removed with an additional babel plugin for production builds).
I'm kinda late to the party but I was wondering this myself. You just have to import your scss module into your test file, like this :
import React from "react";
import { shallow } from "enzyme";
import Example from "./Example";
import styles from "./styles.module.scss
it("example test", () => {
const wrraper = shallow(<Example />);
wraper.find(`.${styles.button}`).simulate("click");
expect("test somethig");
});