how to get click working in redux-react component with enzyme - react-redux

Try to test click event to increase a value in the redux state. But the counter value is always 0.
Counter render
render() {
const { counter, label, isSaving, isLoading, error } = this.props
return <form>
<legend>{label}</legend>
<pre>{JSON.stringify({ counter, isSaving, isLoading }, null, 2)}</pre>
<button ref='increment' onClick={this._onClickIncrement}>click me!</button>
<button ref='save' disabled={isSaving} onClick={this._onClickSave}>{isSaving ? 'saving...' : 'save'}</button>
<button ref='load' disabled={isLoading} onClick={this._onClickLoad}>{isLoading ? 'loading...' : 'load'}</button>
{error ? <div className='error'>{error}</div> : null}
</form>
}
Jest Test
let container, store
beforeEach(() => {
store = mockStore(initialState)
container = shallow(<Counter label='a counter!' store={store} />)
})
it('+++ check Props after increments counter', () => {
const mockClick = jasmine.createSpy('click');
expect(container.find('button').at(0).type()).toEqual('button');
const increment = container.find('button').at(0)
container.find('button').at(0).simulate('click')
container.find('button').at(0).simulate('click')
container.find('button').at(0).simulate('click')
const pre = container.find('pre');
// const pre = TestUtils.findRenderedDOMComponentWithTag(counter, 'pre')
console.log(container.props().counter);
// expect(JSON.parse(counter.find('pre').first.text()).counter.value).toEqual(3)
})

Related

React Hook / How to pause & continue my timer?

Hello
I am working on a timer in React to understand how Hooks works, and so far everything is ok except the start button (in my case the timer starts automatically and start button should be use with pause). I can't figure how to resolve this problem with these hooks.
const { useRef, useState, useEffect } = React;
function Minuteur() {
const intervalRef = useRef();
const [timer, setTimer] = useState(30);
useEffect(() => {
const id = setInterval(() => {
setTimer((oldTimer) => oldTimer - 1);
}, 1000);
intervalRef.current = id;
}, []);
const stopTimer = () => {
clearInterval(intervalRef.current);
};
const resetTimer = () => {
setTimer(30)
};
const playTimer = () => {
};
return (
<div>
<p>Il reste : {timer} secondes</p>
<button onClick={playTimer}> PLAY! </button>
<button onClick={stopTimer}> STOP! </button>
<button onClick={resetTimer}> RESET! </button>
</div>
);
Codepen
function Minuteur() {
// Définition de la référence
const intervalRef = useRef();
const [timer, setTimer] = useState(30);
const [timerRunning, setTimerRunning] = useState(false); // I added a state for if the timer should be running or not
useEffect(() => {
let interval = null;
if (timerRunning) { // Check if the timer is running
interval = setInterval(() => {
setTimer(timer => timer - 1);
}, 1000);
}
return () => clearInterval(interval);
}, [timerRunning]); // rerun side effect when timerRunning changes
// Fonction permettant d'arrêter le ‘timer’
const stopTimer = () => {
setTimerRunning(false) // Set running to false
};
const resetTimer = () => {
setTimer(30);
stopTimer();
};
const playTimer = () => {
setTimerRunning(true); // set running to true
};
...
}
Edit: Everything in the [] dependency array at the end of the useEffect hook is what the side effect "watches". So by adding the timerRunning to the dependency array the useEffect hook will watch for the timerRunning and when it changes, it will cause the hook to re-render. If it is an empty array then it will only ever run on the initial load. That is why your timer started on refresh.

How to make a rxjs subscription after a specific action in react hooks?

As we all know that we need to make a subscription in useeffect and unsubscribe it when the component will unmount. But this kind of code will be triggered once the component is mounted. I'm now want to trigger the subscription after a specific action.Look at the code below.
const [timing, setTiming] = useState<number>(60)
const interval$ = interval(1000)
useEffect(() => {
})
const sendCodeOnceSubmit = async (phone: number) => {
const res = await sendCode(phone)
if (res.code !== 200) {
message.error(`${res.message}`)
} else {
interval$.pipe(take(60)).subscribe(() => setTiming(timing - 1))
}
}
I have a form in the dom,and once I click submit,the sendCodeOnceSubmit function will be triggered which will then send a request through sendCode function to the server. Once the server return a success code, I want to make a countdown with rxjs, but how can I unsubscribe it cause the normal way to do it is to subscribe a observable in useeffect. Thanks for anyone who can help.
Just wrap interval$ with useState and write a useEffect for it.
// Moved const out of component.
const defaultTiming = 60;
/* ... */
export default function App() {
const [timing, setTiming] = useState<number>(defaultTiming);
const [interval$, setInterval$] = useState<Observable<number> | undefined>();
useEffect(() => {
if (!interval$) return;
const subscription = interval$.pipe(take(defaultTiming)).subscribe(() => {
setTiming((prev) => prev - 1);
});
return () => subscription.unsubscribe();
}, [interval$]);
const sendCodeOnceSubmit = async (phone: number) => {
const res = await sendCode(phone);
if (res.code !== 200) {
// message.error(`${res.message}`);
console.error(res.message);
} else {
setInterval$(interval(1000));
}
};
return (
<div className="App">
<p>{timing}</p>
<button type="button" onClick={() => sendCodeOnceSubmit(123)}>
Click
</button>
</div>
);
}

Setting the state from a callback in the parent component doesn't work

I'm trying to set the state from the parent component using a callback. This callback gets passed down to the child component that renders a material ui datatable. The callback responds onClick and passes some values to the callback. The problem is that setting the state with the values from the callback arguments doesn't work.
My assumption is that when the user clicks the button from the child component, it should invoke the callback function and pass the values I needed to set the state.
Parent Component:
export default function ViewJobs() {
const [type, setType] = useState('');
const [params, setParams] = useState({});
const callback = ({ cellValues, componentType, path }) => {
setType(componentType);
setParams(cellValues) // Sets the params with an object.
console.log(cellValues) // Displays the data I need in the console
history.push(path);
};
console.log(params) // Displays undefine in the console.
return(
<React.Fragment>
<TabPanel value={value} index={0} dir={theme.direction} >
<DataTable
jobs={job}
title='All'
parentCallback={callback}
/>
</TabPanel>
</React.Fragment>
);
}
Child Component
import React, { useEffect } from 'react';
export default function DataTable(props) {
const { jobs, parentCallback } = props;
const rows = jobs.payload;
const handleDiaryClick = (event, cellValues) => {
const params = {
cellValues,
componentType: 'diary',
path: "/view/jobs/diary"
};
parentCallback(params);
};
const renderDiaryElement = params => {
return (
<Button
variant="contained"
color="primary"
style={{ backgroundColor: "#000000" }}
onClick={(event) => {
handleDiaryClick(event, params);
}}
>
<MenuBookIcon />
</Button>
);
}
return (
<div
className={classes.root}
style={{ height: 400, width: '100%' }}
>
<DataGrid
rows={rows}
columns={columns}
pageSize={5}
//checkboxSelection
disableSelectionOnClick
/>
</div>
);
}
Since the state has been lifted up to the parent component, I'm under the impression that the code above should be working.
I tried to reproduce the issue but I couldn't replicate it.
Any advice or inputs are appreciated. Thanks.
After further checking on my codebase, I found that the history.push(path) located in my callback is causing the issue. I had to remove this line of code for it to work.

mapDispatchToProps not updating store

I'm working on a personal project with redux. My mapStateToProps function seems to me properly written. but when I try to use it to send an object to my store nothing works.
Here's my function:
const mapDispatchToProps = dispatch => {
return {
addOrder: (item) => {
dispatch(addOrder(item));
}
}
}
<div className="recordOrder">
<button onclick={() => this.props.addOrder(this.state)}>Enregistrer et lancer la commande</button>
</div>
And my reducer:
const initialState = {
orderList : []
}
console.log(initialState);
export default function rootReducer ( state= initialState, action){
const orderList = [...state.orderList];
let position
switch (action.type){
case ADD_ORDER:
return {
orderList : [...state.orderList, action.payload]
};
case DELETE_ORDER:
position = orderList.indexOf(action.payload)
orderList.splice(position, 1)
return {
orderList
}
default:
return state;
}
console.log(state)
}
My entire component as requested:
import React, { Component } from 'react';
import { NavItem } from 'react-bootstrap';
import menu from './menu';
import { connect } from 'react-redux';
import { addOrder} from '../action'
class getOrder extends Component {
state = {
number: `CMD-${Date.now()}`,
order:[],
total: 0 ,
menu:menu,
isPaid: false
}
addItem = (index) => {
const order = [...this.state.order];
const menu = [...this.state.menu];
let total = this.state.total;
const pizza = menu[index];
console.log(pizza);
let ind = order.findIndex((item) =>
item.article == pizza.name
)
if (ind === -1){
order.push({article: pizza.name, price: pizza.price, volume:1})
total = total + order[order.length-1].price
} else if (ind != -1){
order[ind].volume++
total = total + order[ind].price
}
this.setState({
order:order,
total:total
})
console.log("youpiii");
console.log(this.state.total);
console.log(this.state.order);
}
render() {
const menuDisplay= menu.map( (item) => {
return (
<div>
<img onClick={() => this.addItem(item.number)} src={`${process.env.PUBLIC_URL}${item.picture}`} alt="picture" />
<div className="tagPrice">
<p>{item.name}</p>
<p>{item.price} €</p>
</div>
</div>
)
});
const currentOrder = [...this.state.order]
const orderDisplay = currentOrder.map((item) => {
let price = item.price*item.volume;
console.log(price);
return (
<div>
<h1>{item.volume} × {item.article}</h1>
<p>{price} €</p>
</div>
)
} );
return (
<div className="takeOrder">
<div className="orderban">
<h1>Pizza Reflex</h1>
</div>
<div>
<div className="menuDisplay">
{menuDisplay}
</div>
<div className="orderBoard">
<h1>Détail de la commande N°{this.state.number}</h1>
{orderDisplay}
<div className="total">
<h2>Soit un total de {this.state.total} € </h2>
</div>
<div className="recordOrder">
<button onclick={() => this.props.addOrder(this.state)}>Enregistrer et lancer la commande</button>
</div>
</div>
</div>
</div>
);
}
}
const mapDispatchToProps = dispatch => {
return {
addOrder: (item) => {
dispatch(addOrder(item));
}
}
}
export default connect ( mapDispatchToProps) (getOrder);
Can someone tell me what I've missed ?
Thanks for your help !
What you are missing is more of your code it can not be solved with what you have.
In more details what I need is the this.state , combinedReducer
The easiest fix you can do now is changing yow mapDispatchToProps works better if it is an obj
const mapStateToProps = (state) => {
return {
// here you specified the properties you want to pass yow component fom the state
}
};
const mapDispatchToProps = {action1, action2};
export default connect ( mapDispatchToProps) (getOrder);
connectreceives two params mapStateToProps and mapDispatchToProps,
mapDispatchToProps is optional, but mapStateToProps is mandatory, there for you need to specified, if your are not going to pass anything you need to pass a null value
export default connect (null, mapDispatchToProps) (getOrder);
also avoid exporting components without a name
example
function MyButton () {}
const MyButtonConnect = connect(state, dispatch)(MyButton);
export default MyButtonConnect

React update values

I've got problem with my first react app.
I've set the interval function which counts down from 10 to 0 and after the 0 is reached the interval is cleared. At least it should work like this, but when I console log the time it's always 10 (even though it renders properly in the browser - the value is getting smaller), so it never jumps to the else statement.
What should I do to fix this problem?
const {useState} = React;
const Timer = () => {
let flag = true;
const [time, setTime] = useState(10);
const handleClick = () => {
if (flag) {
setInterval(counter, 500);
}
}
const counter = () => {
if (time > 0) {
console.log(time);
setTime(time => time - 1);
} else {
console.log('out');
clearInterval(timer);
}
}
return(
<div>
<div>{time}</div>
<button className="start" onClick={handleClick}>START</button>
</div>
)
}
ReactDOM.render(
<Timer />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>
I managed to solve this problem. Thank you guys for trying to help :) Snippet below:
const {useState} = React;
const {useEffect} = React;
const Timer = () => {
const [flag, setFlag] = useState(false);
const [time, setTime] = useState(10);
const handleClick = () => {
setFlag(!flag);
}
useEffect(() => {
function counter () {
if (time > 0) {
setTime(time => time - 1)
}
}
if (flag) {
console.log('a');
const interval = setInterval(counter, 1000)
return () => clearInterval(interval);
}
}, [flag, time]);
return(
<div>
<div>{time}</div>
<button className="start" onClick={handleClick} >START</button>
</div>
)
}
ReactDOM.render(
<Timer />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>

Resources