Add a onClick event to a custom React Component - react-component

I have a React Component ValidationEditor that calls another custom React Component Property, which holds the property value a array of strings.
interface props extends ValidationDataProperty {
tree: Tree;
}
const ValidationEditor = ({ tree, id, ifData, thenData, elseData }: props) => {
const { classes } = useStyles();
const { state, dispatch } = useContext(PropertyContext);
const updateValidation = modifyConditionalProperty(id, dispatch);
return (
<Group className={classes.validator}>
<Box></Box>
<Text>If</Text>
<Property
value={[""]}
mode="edit"
tree={tree}
onClick={(e: { target: { value: SetStateAction<string[]> } }) =>
updateValidation({ ifData: { ...ifData, value: e.target.value } })
}
/>
<NativeSelect
data={_.keys(ComparisonType)}
required
value={ifData.comparison}
onChange={(e: { target: { value: SetStateAction<string> } }) =>
updateValidation({
ifData: { ...ifData, comparison: e.target.value },
})
}
/>{" "}
<TextInput
placeholder="Provide conditional value"
required
value={ifData.value}
sx={{ flex: 1 }}
onChange={(e: { target: { value: SetStateAction<string> } }) =>
updateValidation({ ifData: { ...ifData, value: e.target.value } })
}
/>
</Group>
);
};
export default ValidationEditor;
Now I want to add a onClick event to the Property React Component . Basically onClick I want to call an action -> modifyConditionalProperty(id, dispatch) , that will update the store via a reducer. I am only struggling to make it work only for my custom React component Property rest its working fine.
This is how the Property component looks like
interface PropertyProps {
value?: string[];
mode: "edit" | "display";
tree: Tree;
onClick?: (e: { target: { value: SetStateAction<string[]> } }) => void;
}
const Property = ({ value, mode, tree }: PropertyProps) => {
const [currentValue, setCurrentValue] = useState<string[]>(value || []);
const [displayMode, toggle] = useToggle(mode, ["edit", "display"]);
console.log(value) // ["a", "b", "c"]
return (
<Box>
.....
</Box>
);
};
export default Property;

Related

TypeError: dispatch is not a function when clicking the toggle button

I am using react redux-thunk. I have a set of users data that I get from an API and this is the schema:
.
I've connected the "active" property with the checked attribute of a Switch MUI button, so naturally when calling the API I have some users with their switch button already on "true". What I am trying to do is to just make the switch functional, and just be able to click it and change its state, not necessarily doing anything with that.
Here's my toggleType.js:
export const TOGGLE = "TOGGLE";
Here's my toggleAction.js:
import { TOGGLE } from "./toggleType";
const statusToggleAction = () => {
return {
type: TOGGLE,
};
};
export const statusToggle = () => {
return (dispatch) => {
dispatch(statusToggleAction);
};
};
Here's my toggleReducer.js:
import { TOGGLE } from "./toggleType";
const initialState = {
status: false,
};
const toggleReducer = (state = initialState, action) => {
switch (action.type) {
case TOGGLE:
status: true;
default:
return state;
}
};
export default toggleReducer;
Everything is under my userContainer.js, like that:
function UserContainer({ userData, fetchUsers }) {
useEffect(() => {
fetchUsers();
}, []);
return userData.loading ? (
<h2>Loading</h2>
) : userData.error ? (
<h2>{userData.error}</h2>
) : (
<Container maxWidth="lg" style={{ flexGrow: 1, height: "100%" }}>
<h2>User List</h2>
<div>
{userData &&
userData.users &&
userData.users.map((user) => (
<div key={user.id}>
<p>{user.name}</p>
<Switch checked={user.active} onChange={statusToggle()} />
</div>
))}
</div>
</Container>
);
}
const mapStateToProps = (state) => {
return { userData: state.user, statusToggle: state.status };
};
const mapDispatchToProps = (dispatch) => {
return {
fetchUsers: () => dispatch(fetchUsers()),
statusToggle: () => dispatch(statusToggle()),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(UserContainer);
This is the error I am getting whenever I am clicking one of those switches:
Any ideas are welcome, I "learned" redux like 3 days ago!
toggleReducer function in toggleReducer.js, replace status: true; with return { status: true }.
Just return action in statusToggle function in toggleAction.js without dispatch as following.
export const statusToggle = () => {
return statusToggleAction();
};
Or just call statusToggleAction directly in userContainer.js as following.
export const statusToggle = () => {
return (dispatch) => {
dispatch(statusToggleAction());
};
};

How to call a function from another component

I am using Alan AI voice assistant, so I am trying to trigger a function from another component based on the voice command.
This is the component holding the function I want to call
const CartButton: React.FC<CartButtonProps> = ({
className,
isShowing,
}) => {
const { openDrawer, setDrawerView } = useUI();
function handleCartOpen() {
setDrawerView('CART_SIDEBAR');
isShowing;
return openDrawer();
}
return (
<button
className={cn(
'flex items-center justify-center',
className
)}
onClick={handleCartOpen}
aria-label="cart-button"
>
</button>
);
};
export default CartButton;
So in the component above I want to use the handleCartOpen function in the below component
const COMMANDS = {
OPEN_CART: "open-cart",
}
export default function useAlan() {
const [alanInstance, setAlanInstance] = useState()
const openCart = useCallback(() => {
alanInstance.playText("Opening cart")
// I want to call the handleCartOpen function here
}, [alanInstance])
useEffect(() => {
window.addEventListener(COMMANDS.OPEN_CART, openCart)
return () => {
window.removeEventListener(COMMANDS.OPEN_CART, openCart)
}
}, [openCart])
useEffect(() => {
if (alanInstance != null) return
const alanBtn = require('#alan-ai/alan-sdk-web');
setAlanInstance(
alanBtn({
key: process.env.NEXT_PUBLIC_ALAN_KEY,
rootEl: document.getElementById("alan-btn"),
onCommand: ({ command, payload }) => {
window.dispatchEvent(new CustomEvent(command, { detail: payload }))
}
}));
}, []);
}
So in the openCart Callback, i want to trigger the handleCartOpen function which is in the first component

Calling a function from another component with redux

Trying to toggle open a modal from another component with redux. Almost there but not really sure how to finish it up - been looking around for a clear answer!
On the HomeScreen component (the main component), to activate the openModal method on the AddCircleModal component, causing the Modal to open.
The Modal - AddCircleModal: Using redux, I can successfully close the modal if I open it manually in the code
class AddCircleModal extends React.Component {
state = {
top: new Animated.Value(screenHeight),
modalVisible: false
}
// componentDidMount() {
// this.openModal()
// }
openModal = () => {
Animated.spring(this.state.top, {
toValue: 174
}).start()
this.setState({modalVisible: true})
}
closeModal = () => {
Animated.spring(this.state.top, {
toValue: screenHeight
}).start()
this.setState({modalVisible: false})
}
render() {
return (
<Modal
transparent={true}
visible={this.state.modalVisible}
>
<AnimatedContainer style={{ top: this.state.top, }}>
<Header />
<TouchableOpacity
onPress={this.closeModal}
style={{ position: "absolute", top: 120, left: "50%", marginLeft: -22, zIndex: 1 }}
>
<CloseView style={{ elevation: 10 }}>
<FeatherIcon name="plus" size={24} />
</CloseView>
</TouchableOpacity>
<Body />
</AnimatedContainer>
</Modal>
)
}
}
function mapStateToProps(state) {
return { action: state.action }
}
function mapDispatchToProps(dispatch) {
return {
closeModal: () =>
dispatch({
type: "CLOSE_MODAL"
})
}
}
export default connect(mapStateToProps, mapDispatchToProps)(AddCircleModal)
HomeScreen: The other component that I want to toggle from
//redux
import { connect } from 'react-redux'
import styles from './Styles'
class HomeScreen extends React.Component {
constructor() {
super();
this.state = {
};
}
toggleOpenCircleModal = () => {
// this.openModal() - what do I do with this to call the openModal function in the modal component?
console.log('owwwww weeeee')
}
render() {
return (
<SafeAreaView>
<HomeHeader openModal={this.toggleOpenCircleModal}/> - this method is because I'm calling toggleOpenCircleModal from a button in the header of the home screen. It works as it outputs the 'owwwww weeeee' string to the console.
<SafeAreaView style={{ width: '100%', flex: 1}} />
<AddCircleModal />
</SafeAreaView>
);
}
}
function mapStateToProps(state) {
return { action: state.action }
}
function mapDispatchToProps(dispatch) {
return {
openModal: () =>
dispatch({
type: "OPEN_MODAL"
})
}
}
export default connect(mapStateToProps, mapDispatchToProps)(HomeScreen)
modalToggle: The reducer
const initialState = {
action: ""
}
const modalToggle = (state = initialState, action) => {
switch (action.type) {
case "OPEN_MODAL":
return { ...state, action: "openModal" }
case "CLOSE_MODAL":
return { ...state, action: "closeModal" }
default:
return state
}
}
export default modalToggle
Right now, your components are not using redux store properly.
When you use mapStateToProps, you can access every redux store reducer. You can access every prop in them and these will be sent via props in your connected component. For instance:
//redux
import { connect } from 'react-redux'
import styles from './Styles'
class HomeScreen extends React.Component {
constructor() {
super();
this.state = {
};
}
toggleOpenCircleModal = () => {
if(this.props.action === 'openModal') {
this.props.openModal();
} else {
this.props.closeModal();
}
}
render() {
const { action } = this.props; // this.props.action is coming from Redux Store
return (
<SafeAreaView>
{action} // this will be 'openModal'
</SafeAreaView>
);
}
}
function mapStateToProps(state) {
return { action: state.action } // this will be available in HomeScreen as props.action
}
function mapDispatchToProps(dispatch) {
return {
openModal: () =>
dispatch({
type: "OPEN_MODAL"
})
}
}
export default connect(mapStateToProps, mapDispatchToProps)(HomeScreen)
You can read more on https://react-redux.js.org/using-react-redux/connect-mapstate.
The same goes for mapDispatchToProps. In your case, openModal will be available in props.openModal in your HomeScreen component. You can read more on https://react-redux.js.org/using-react-redux/connect-mapdispatch
Based on this, in your AddCircleModal component, you should be using props.action to evaluate if the modal should be visible. (props.action === 'openModal').
If you want to open or close your modal, you'll just need to call the openModal or closeModal dispatch call in your component. In HomeScreen component, in your function toggleOpenCircleModal, you will call openModal() or closeModal() depending on props.action === 'openModal'.
Lastly, you should be using just a boolean value to check for the modal visibility, instead of a string, if that's the only purpose for your reducer.
const initialState = false;
const modalToggle = (state = initialState, action) => {
switch (action.type) {
case "OPEN_MODAL":
return true;
case "CLOSE_MODAL":
return false;
default:
return state
}
}
export default modalToggle

Redux store doesn't update component props

I'm trying to call a API from my store to update the state of a component, here getting the price of a crypto-curency.
I use a clone of my state in return (nextState here) and the log of nextState is well fill with goods price, but my component get only the initialState.
Here the code :
My component
import React from 'react';
import { StyleSheet, Text, View, Button, ImageBackground,TouchableOpacity, Image } from 'react-native';
import {widthPercentageToDP as wp, heightPercentageToDP as hp} from 'react-native-responsive-screen';
import { connect } from 'react-redux'
class Bitcoin extends React.Component {
constructor(props){
super(props);
this.state = {
}
}
componentDidMount() {
const action = { type: 'PRICES', value: this.state.cryptos}
this.props.dispatch(action)
console.log(this.props.cryptos)
}
componentDidUpdate() {
console.log("Component did Update : ")
console.log(this.props.cryptos)
}
render() {
return (
<View>
<Text style={styles.title}>Bitcoin !</Text>
<Text> {this.props.cryptos[0].price} </Text>
</View>
)
}
}
const styles = StyleSheet.create({
title: {
marginTop: wp("10%")
},
});
const mapStateToProps = (state) => {
return {
cryptos: state.Crypto.cryptos
}
}
const mapDispatchToProps = (dispatch) => {
return {
dispatch: (action) => { dispatch(action) }
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Bitcoin)
My Reducer :
const initialState = { cryptos: [
{
title: "Bitcoin",
id: "BTC",
price: 0
}, {
title: "Ethereum",
id: "ETH",
price: 0
}, {
title: "Ripple",
id: "XRP",
price: 0
}], toast: 0}
function Crypto(state = initialState, action) {
let nextState
switch (action.type) {
case 'PRICES':
nextState = {...state}
fetch('https://min-api.cryptocompare.com/data/pricemulti?fsyms=ETH,BTC,XRP&tsyms=EUR&api_key=c3b60840403013f86c45f2ee97571ffdf60072fafff5c133ed587d91088451b6')
.then((response) => response.json())
.then((responseJson) => {
nextState.cryptos[0].price = responseJson.BTC.EUR.toString()
nextState.cryptos[1].price = responseJson.ETH.EUR.toString()
nextState.cryptos[2].price = responseJson.XRP.EUR.toString()
console.log("NextState :");
console.log(nextState.cryptos);
return nextState
})
.catch((error) => {
console.error(error);
});
return nextState
case 'TOAST':
nextState = {...state}
default:
return state
}
}
export default Crypto
Welcome to StackOverflow.
I guess you are new to Redux workflow. So here it is.
Actions describe an action. The reducer receive the action and specify how the store is changing.
Action must be plain javascript object. And reducer functions must be pure !
Here what is forbidden to do inside reducers :
Mutate its arguments;
Perform side effects like API calls and routing transitions;
Call non-pure functions, e.g. Date.now() or Math.random().
In your example, by calling fetch. You're making an API Call.
I invite you to read this guide to know more about : How to introduce API call and asynchronous into your redux app. (https://redux.js.org/advanced/async-actions)

React native component function promise return url onNavigationStateChange

I have a component, this component is nothing but a WebView.
I make a call to this component and I want a result back, through promises.
I have to make sure that after the WebView is loaded and running onNavigationStateChange, I have to return a Promise to return the result.
Main:
import * as React from 'react';
import { View } from 'react-native';
import ShortUrl from './ShortUrl';
export default class App extends React.Component {
componentDidMount() {
this.shortUrl
.init('https://www.cineblog.life/?trdownload=0&trid=24045&movie=0')
.then(uid => {
console.log('URL: ' + uid);
})
.catch(err => alert('error: ' + err));
}
render() {
return (
<View>
<ShortUrl
ref={r => (this.shortUrl = r)}
style={{ width: 0, height: 0, backgroundColor: '#000' }}
/>
</View>
);
}
}
ShortUrl:
import * as React from 'react';
import { View, WebView } from 'react-native';
export default class ShortUrl extends React.Component {
constructor() {
super();
this.state = {
initialUrl: '',
init: false,
//promise:
};
}
init(initialUrl) {
this.setState({ initialUrl, init: true });
return new Promise(async (resolve, reject) => {
resolve('OK');
});
}
onNavigationStateChange = navState => {
const { initialUrl } = this.state;
if (initialUrl !== navState.url) {
return new Promise(async (resolve, reject) => {
resolve(navState.url);
});
}
};
render() {
const { initialUrl, init } = this.state;
if (!init) return null;
return (
<View>
<WebView
source={{
uri: initialUrl,
}}
onNavigationStateChange={this.onNavigationStateChange}
style={{ flex: 1 }}
/>
</View>
);
}
}
Link expo: Here
I can't fully understand your question, but it seems you're simply looking for a function passed as prop to your child component. Once your promise is completed just call that function you have in prop and pass it a parameter, in order to handle it in the parent component.
In your Main:
[...]
<ShortUrl
ref={r => (this.shortUrl = r)}
style={{ width: 0, height: 0, backgroundColor: '#000' }}
handleResult={ resultArrivingFromChild => { // do what you want with the result }}
/>
In ShortUrl:
onNavigationStateChange = navState => {
[...] // do what you have to do
this.props.handleResult(resultYouWantToSendBack);
};

Resources