I am using 2 useState in my code. When 2nd useSate status is true I want 1st useState status to be made false.
I can achieve it using class components not via functional component.
Class Component Code
class App extends React.Component {
state = { visible: false, childrenDrawer: false };
showDrawer = () => {
this.setState({
visible: true,
});
};
onClose = () => {
this.setState({
visible: false,
});
};
showChildrenDrawer = () => {
this.setState({
childrenDrawer: true,
visible: false, //**this make 1st state status false when 2nd state is true**
});
};
onChildrenDrawerClose = () => {
this.setState({
childrenDrawer: false,
});
};
How I can achieve this in the functional component please guide
const [visible, setVisible] = useState(false);
const [hideAuto, setAuto]= useState(false);
const showDrawer = () => {
setVisible(true);
};
const onClose = () => {
setVisible(false);
};
const Quality=()=>{
setAuto(true);
}
const Selection = () => {
setVisible(false);
};
You can initialize and update state in function components using React hooks just like you would in your class.
In your functional component, why are you using two separate hooks? Why not just track state like you did in your class, ie. using a single object. For example:
let [myState, updateMyState] = useState({ visible: false, childrenDrawer: false });
Then to update the state:
updateMyState(...myState, visible: true) // show drawer for example
How about this simple setup, where you pass the function that changes parent state as props to the child component:
const parentDrawer = () => {
const [parentState, setParentState] = useState(true);
const onChildClose = () => {
setParentState(false)
}
return <childDrawer onChildClose={onChildClose} />
}
const childDrawer = ({ onChildClose }) => {
const onChildInteraction = () => {
onChildClose()
}
return (...)
}
This covers the case in which the child is rendered/returned by the parent. If you have them separately, maybe look into context.
Related
I am trying to set mockPatient data and wanted to test if the 'sortByCaseFn ' function is called by the useEffect.
Here is my sourcecode:
Patient.tsx
const [patients, setPatients] = useState([]);
const [sortBy, setSortBy] = useState('events');
const [fetched, setFetched] = useState(false);
const dispatch = useAppDispatch();
const props = useAppSelector((state) => state.myPatientProps);
const getPatientData = (): void => {
dispatch(MyPatientActions.getMyPatientsData());
};
const sortByCaseFn = (sortBy, list) => {
let patientsToSort = [...list];
if (sortBy.includes('events'))
patientsToSort.sort(
sorter.byPropertiesOf(['-ActiveEventsCount', 'LastName'])
);
if (sortBy.includes('vae'))
patientsToSort.sort(sorter.byPropertiesOf(['-VaeStatus']));
console.log('patientsToSort---', patientsToSort);
setPatients(patientsToSort);
};
useEffect(() => {
if (!fetched) {
getPatientData();
}
}, []);
useEffect(() => {
console.log('setpatients called .. ', patients);
}, [patients]);
useEffect(() => {
const saved_sortby = localStorage.getItem('sortby');
if (saved_sortby) {
sortByCaseFn(saved_sortby, props.myPatientDetails);
} else sortByCaseFn('events', props.myPatientDetails);
setFetched(true);
}, [props.myPatientDetails]);
useEffect(() => {
sortByCaseFn(sortBy, patients);
}, [sortBy]);
return (
<> Render Patient List </> )
My Test Code :
Patients.test.tsx
jest.mock('react-redux', () => ({
useSelector: jest.fn(),
useDispatch: jest.fn()
}));
export const setHookTestState = (newState: any) => {
const setStateMockFn = () => {};
return Object.keys(newState).reduce((acc, val) => {
acc = acc?.mockImplementationOnce(() => [newState[val], setStateMockFn]);
return acc;
}, jest.fn());
};
describe('My Patient Screen', () => {
const useSelectorMock = reactRedux.useSelector as jest.Mock<any>;
const useDispatchMock = reactRedux.useDispatch as jest.Mock<any>;
beforeEach(() => {
useSelectorMock.mockImplementation((selector) => selector(mockStore));
useDispatchMock.mockImplementation(() => () => {});
});
afterEach(() => {
useDispatchMock.mockClear();
useSelectorMock.mockClear();
});
const mockInitialState = {
myPatientDetails: vaeMock,
fetching: false,
failedMsg: '',
requestPayload: {}
};
const mockStore = {
counter: undefined,
menu: undefined,
selectPatientProps: undefined,
myPatientProps: mockInitialState
};
test('validate sorting by events', async (done) => {
React.useState = setHookTestState({
patients: vaeMock,
sortBy: 'vae',
fetched: 'false'
});
const {
getByText,
getByRole,
getByTestId,
getAllByTestId,
findAllByTestId,
queryByText,
container
} = render(<Mypatient />);
await waitFor(() => {
expect(getByText('Ander, Sam')).toBeDefined();
});
const list = getAllByTestId('patientname');
expect(within(list[0]).getByText('Sara, Jone')).toBeInTheDocument(); //Fails here as Sorting doesnt happen
console.log('....list ', list);
});
});
My Observations:
The 'vaeMock' data that I set in redux state 'mockInitialState' is successfully sent as props
The 'vaeMock' data that I set in component state using setHookTestState is also set successfully.
The lifecycle events happens like this -
a. setPatients() is called using the component state data.
b. using props that is sent , sortByCaseFn is called but setPatients is not called.
c. again using the component state , sortByCaseFn is called but setPatients is not set.
Without setting the component state variables runs into a TypeError: Undefined is not iterable.
All Iam trying to do is - send a mockData to a component that uses useDispatch, useEffects
and sort the data on the component mount and initialize to local state variable.
class Dashboard extends Component {
constructor(props) {
super(props)
this.state = {
assetList: [],
assetList1: [];
}
}
componentDidMount = async () => {
const web3 = window.web3
const LandData=Land.networks[networkId]
if (LandData) {
const landList = new web3.eth.Contract(Land.abi, LandData.address)
this.setState({ landList })
}
}
...
}
In this code the state for landlist is not defines in constructor but setState is used. If I have to convert the code to a function component, what will be the equivalent code?
In React class components, there existed a single state object and you could update it with any properties you needed. State in React function components functions a little differently.
React function components use the useState hook to explicitly declare a state variable and updater function.
You can use a single state, and in this case the functionality would be pretty similar, keeping in mind though that unlike the this.setState of class components, the useState
Example:
const Dashboard = () => {
const [state, setState] = React.useState({
assetList: [],
assetList1: []
});
useEffect(() => {
const web3 = window.web3;
const LandData = Land.networks[networkId];
if (LandData) {
const landList = new web3.eth.Contract(Land.abi, LandData.address);
setState(prevState => ({
...prevState,
landList,
}));
}
}, []);
return (
...
);
};
With the useState hook, however, you aren't limited to a single state object, you can declare as many state variables necessary for your code to function properly. In fact it is recommended to split your state out into the discrete chunks of related state.
Example:
const Dashboard = () => {
const [assetLists, setAssetLists] = React.useState({
assetList: [],
assetList1: []
});
const [landList, setLandList] = React.useState([]);
useEffect(() => {
const web3 = window.web3;
const LandData = Land.networks[networkId];
if (LandData) {
const landList = new web3.eth.Contract(Land.abi, LandData.address);
setLandList(landList);
}
}, []);
return (
...
);
};
const Dashboard = () => {
const [assetList, setAssetList] = useState([])
const [assetList1, setAssetList1] = useState([])
useEffect(() => {
const web3 = window.web3
const LandData = Land.networks[networkId]
if (LandData) {
const landList = new web3.eth.Contract(Land.abi, LandData.address)
setAssetList(landList)
}
}, [])
I have a class component that's empowered with the HOC withRouter, and connect() to have dispatch available in props
export default withRouter(connect()(MyComponent));
This component is coded as a class component, now, I changed the component to a functional component:
Changed the function header from class "MyComponent expands..." to "const MyComponent = props => {..."
Changed the way the state is created, by using "const [state, setState] = useState(..."
Instead of coding componentDidMount to perform initial operations, I use
useEffect(() => {
getData()
}, []);
Where getData is:
const getData = props => {
const getDocument = async () => {
const {
dispatch,
location: { search }, // Here I get an error, search and
match: { params }, // params are undefined
} = props;
...
}
Changed every function definition, from "handleSuccessAction = message => {..." to "const handleSuccessIndex = response => {..."
Changed all reference to functions from "this.myFunction();" to "myFunction()"
And leaved export default withRouter(connect()(MyComponent)) as is
Begin EDIT:
here is the whole code, once turned into a functional component
------------- all imports ------------------
const DocumentPreview = props => {
const [state, setState] = useState({
document: {
documentType: '',
name: '',
file: {
fileUrl: '',
filename: ''
},
},
file: {
fileUrl: '',
filename: ''
},
});
useEffect(() => {
getDocument();
}, []);
const getDocument = async () => {
const {
dispatch,
location: { search },
match: { params },
} = props;
const options = parseUrlParams(search);
setState({ onRequest: true });
showDocumentPreviewRequest(params.id, {
employeeId: options.employee,
dispatch,
successCallback: handleSuccessIndex
});
};
const handleSuccessIndex = response => {
const { data } = response;
const document = camelCaseRecursive(data);
const file = document.fileInfo;
setState({
document,
file,
onRequest: false
});
};
const { onRequest, document, file, modalBody, modalShow, modalTitle, defaultModalShow } = state;
const { pendingToApprove, workflowRequest } = document;
return (
<>
------- Do my stuff -------------
</>
);
}
export default withRouter(connect()(DocumentPreview))
End EDIT
Why do I get this error?, am I doing it wrong?
I'm changing it to a functional component since I have to load the component on a modal, and as I won't be using router and render props, I think I can get this props by using hooks
Rafael
I am trying to test a dispatch from 'mapDispatchToProps' defined with a functional component which uses useEffect() hook and the function is called there.
export const MyComponent = (props) => {
useEffect(() => {
// Anything in here is fired on component mount.
props.registerInStore(props.id, false);
return () => {
// Anything in here is fired on component unmount.
props.resetInStore(props.id);
};
}, []);
const handleOnClick = () => {
props.toggle(props.id);
};
return (
<div >
{!props.isOpen ? (
<button
onClick={handleOnClick}>
Open
</button>
) : (
<button
onClick={handleOnClick}>
close
</button>
)}
</div>
);
};
const mapDispatchToProps = (dispatch) => ({
registerInStore(id, isOpen) {
dispatch(registerInStore(id, isOpen));
},
resetInStore(id) {
dispatch(resetInStore(id));
}
});
export default connect(null, mapDispatchToProps)(MyComponent);
In my unit tests with Mocha and enzyme i also want to test the dispatches inside 'mapDispatchToProps', what i did below does not seem to work :
describe('<MyComponent/>', () => {
let store = mockStore({
toggles: [
{
id: 10,
isOpen: true
}
]
}
});
const options = {
context: {store},
childContextTypes: {store: PropTypes.object.isRequired},
lifecycleExperimental: true
};
const setup = (inputProps = {}) => {
const props = {
id: 10,
isOpen: false,
registerInStore: expect.createSpy(),
resetInStore: expect.createSpy(),
toggle: expect.createSpy(),
...inputProps
};
const wrapper = mount(<MyComponent {...props} />, options);
return {
props,
wrapper
};
};
afterEach(() => {
expect.restoreSpies();
});
it('should dispatch', async () => {
const {wrapper}=setup();
await store.dispatch(wrapper.prop('registerInStore')(10,false));
/* i tried the commented way too instead of directly dispatching*/
// wrapper.prop('registerInStore')(10,false);
//await new Promise((resolve) => setTimeout(resolve, 50));
const expectedActions = [{type: 'REGISTER_IN_STORE', id: 10, isOpen: false}];
expect(store.getActions()).toEqual(expectedActions);
});
the store.getActions() is returning an empty array, i am new to React Hooks and testing, what am i doing wrong, any other solutions?.
Thanks in Advance.
worked by removing the spies e.g:-
const setup = (inputProps = {}) => {
const props = {
id: 10,
isOpen: false,
registerInStore:()=>null,
resetInStore: ()=>null,
toggle: ()=>null,
...inputProps
};
const wrapper = mount(<MyComponent {...props} />, options);
return {
props,
wrapper
};
};
I have a react component that contains the method with setState with a callback. I need to rewrite it to hooks. Please tell me how can i rewrite this method ?
beforeSubmitModal = action => (args) => {
this.setState({
visible: false,
selectedMenuItem: null,
companyCodeModal: {}
}, () => action(args));
};
const onDeleteCode = (id) => {
dispatch(actions.deleteCode.request({ codeId: id }));
};
const modalProps = {
onSaveOrUpdate: beforeSubmitModal(dispatch(actions.insertOrEditCode.request())),
onDelete: beforeSubmitModal(onDeleteCode),
};
you will need to use useEffect to do this
const [visible,setVisible] = useState(ture);
const doSomething = () => {
setVisible(false);
}
useEffect(() => {
//this will render every time the visible state changes
}, [visible]);
to define the states in hooks
const [visible,setVisible]=useState(false) // initial value false
const [selectedMenuItem,setCompanyCodeModal]=useState(null) // initial value null
const [companyCodeModal,setCompanyCodeModal]=useState('')
you need when they change do some action
useEffect(()=> doSomething() ,[visible,selectedMenuItem,companyCodeModal])