How can I call some code in another function after state change? - reactjs

const App = (): JSX.Element => {
const [isShow, setIsShow] = useState(true)
useEffect(() => {
console.log(`is show: ${isShow}`)
},[isShow])
const handleClick = () => {
console.log('call setIsShow')
setIsShow(!isShow)
}
const onClick = () => {
$('.bt1').trigger('click') // click on the button with the class name is `bt1`
// call below code after state `isShow` change
console.log('hello world')
...
}
return (
<div>
<div className='container'>
<div style={{height: '200px', backgroundColor: 'red'}}/>
{isShow && <div className='green_container' style={{height: '200px', backgroundColor: 'green'}}/>}
<div style={{height: '200px', backgroundColor: 'blue'}}/>
</div>
<button onClick={handleClick} className='bt1'>Click...</button>
<button onClick={onClick}>GOGO</button>
</div>
);
};
on the above code, My action is...
click button GOGO
$('.bt1').trigger('click') is calling
function handleClick is calling for print message and change IsShow state
My Question is:
How can I print hello world and another command that below line $('.bt1').trigger('click') inside onClick function after state isShow change ?

Don't use jquery in react. use the ternary operator to show text in Jsx.
const App = (): JSX.Element => {
const [isShow, setIsShow] = useState(true)
const handleClick = () => {
setIsShow(!isShow)
}
const onClick = ()=>{
setIsShow(false)// if you want.
}
return (
<div>
<div className='container'>
<div style={{height: '200px', backgroundColor: 'red'}}/>
{isShow && <div className='green_container' style={{height: '200px', backgroundColor: 'green'}}/>}
<div style={{height: '200px', backgroundColor: 'blue'}}/>
</div>
<button onClick={handleClick} className='bt1'>Click...</button>
<button onClick={onClick}>{isShow ? "Hello World":"GOGO"}</button>
</div>
);
};

you can simply call handleClick in onClick funtion
sth like this:
const onClick = () => {
handleClick()
// call below code after state `isShow` change
console.log('hello world')
...
}
and use useEffect for listening to any change in isShow
if you only want to click on button you can use ref
I changed your code in any way that might be needed for you
take a look at this one:
import { useState, useRef, useEffect } from "react";
const App = () => {
const [isShow, setIsShow] = useState(true);
const btnRef = useRef();
useEffect(() => {
console.log(`is show: ${isShow}`);
}, [isShow]);
const handleIsShowChanged = () => {
console.log("hello world");
};
const handleClick = () => {
console.log("call setIsShow");
setIsShow((prevState) => {
handleIsShowChanged();
return !prevState;
});
};
const onClick = () => {
btnRef.current.click();
};
return (
<div>
<div className="container">
<div style={{ height: "200px", backgroundColor: "red" }} />
{isShow && (
<div
className="green_container"
style={{ height: "200px", backgroundColor: "green" }}
/>
)}
<div style={{ height: "200px", backgroundColor: "blue" }} />
</div>
<button ref={btnRef} onClick={handleClick} className="bt1">
Click...
</button>
<button onClick={onClick}>{isShow ? "Hello World" : "GOGO"}</button>
</div>
);
};
export default App;

Related

Rich text editor in reactjs

How to create a rich text editor in Reactjs and save the value entered and show it in console/ save the value in a state and send it to php file ?
I have tried the below code but cannot save the value.
import React from 'react'
import { Editor } from "react-draft-wysiwyg";
import "../../node_modules/react-draft-wysiwyg/dist/react-draft-wysiwyg.css";
const TextEditor = () => {
return (
<div>
<Editor
toolbarClassName="toolbarClassName"
wrapperClassName="wrapperClassName"
editorClassName="editorClassName"
wrapperStyle={{ width: 1000, border: "1px solid black" }}
/>
</div>
)
}
export default TextEditor
As far as the documentation goes -> https://jpuri.github.io/react-draft-wysiwyg/#/docs?_k=jjqinp (Section properties, Editor state part)
You have a onChange property from which you can listen to and log the current value inside it:
function App() {
const [first, setfirst] = useState("")
console.log(first.blocks.map(x => x.text))
return (
<div>
<Editor
toolbarClassName="toolbarClassName"
wrapperClassName="wrapperClassName"
editorClassName="editorClassName"
onChange={(e) => setfirst(e)}
/>
</div>
)
}
Or if you wish, you could also log the entire object.
const TextEditor = (props) => {
const [data, setData] = useState("")
const handleChange = (event) => {
setData(event)
}
const handleSubmit = (e) => {
e.preventDefault();
let myPara = data.blocks.map(x => { return x.text })
const sendData = {
pid: props.data,
para: myPara[0] //stores the entire text written in the text
//editor in 'para' which can then be stored
//in a db table
}
axios.post('http://localhost/api/texteditor.php', sendData)
.then((result) => {
if (result.data.Status === 'Invalid') {
alert('Invalid data');
}
else {
alert("Data added successfully");
}
})
}
return (
<div>
<form onSubmit={(e) => { handleSubmit(e) }}>
<Editor
toolbarClassName="toolbarClassName"
wrapperClassName="wrapperClassName"
editorClassName="editorClassName"
wrapperStyle={{ width: 1000, border: "1px solid black" }}
onChange={handleChange}
/>
<input className='save d-flex justify-content-left'
type="submit" name="save" value="Save" style=
{{
backgroundColor: "white", color: "blue",
border: "1px solid blue"
}}
/>
</form>
</div>
)
}
export default TextEditor

Show/Hide multiple elements of each button click with matching indexes in React JS

I have a scenario where I have 2 different components ( buttons and an info container). On each button click I am trying to display each matched info container. I am able to achieve the desired functionality in my buttons, but when I pass state back to my other component I am only able to display the matched index. My desired result is if I clicked a button in my nav and it has an active class all my "info container" should remain visible until the "active" class is toggled/removed.
JS:
...
const useStyles = makeStyles((theme) => ({
root: {
display: "flex",
"& > *": {
margin: theme.spacing(1)
}
},
orange: {
color: theme.palette.getContrastText(deepOrange[500]),
backgroundColor: deepOrange[500],
border: "4px solid black"
},
info: {
margin: "10px"
},
wrapper: {
display: "flex"
},
contentWrapper: {
display: "flex",
flexDirection: "column"
},
elWrapper: {
opacity: 0,
"&.active": {
opacity: 1
}
}
}));
const ToggleItem = ({ onChange, id, styles, discription }) => {
const [toggleThisButton, setToggleThisButton] = useState(false);
const handleClick = (index) => {
setToggleThisButton((prev) => !prev);
onChange(index);
};
return (
<>
<Avatar
className={toggleThisButton ? styles.orange : ""}
onClick={() => handleClick(id)}
>
{id}
</Avatar>
{JSON.stringify(toggleThisButton)}
{/* {toggleThisButton && <div className={styles.info}>{discription}</div> } */}
</>
);
};
const ToggleContainer = ({ discription, className }) => {
return <div className={className}> Content {discription}</div>;
};
export default function App() {
const data = ["first", "second", "third"];
const classes = useStyles();
const [value, setValue] = useState(false);
const handleChange = (newValue) => {
setValue(newValue);
console.log("newValue===", newValue);
};
return (
<>
<div className={classes.wrapper}>
{data.map((d, id) => {
return (
<div key={id}>
<ToggleItem
id={id}
styles={classes}
discription={d}
onChange={handleChange}
/>
</div>
);
})}
</div>
<div className={classes.contentWrapper}>
{data.map((d, id) => {
return (
<ToggleContainer
className={
value === id
? clsx(classes.elWrapper, "active")
: classes.elWrapper
}
key={id}
styles={classes}
discription="Hello"
/>
);
})}
</div>
</>
);
}
Codesanbox:
https://codesandbox.io/s/pedantic-dream-vnbgym?file=/src/App.js:0-2499
Codesandbox : https://codesandbox.io/s/72166087-zu4ev7?file=/src/App.js
You can store the selected tabs in a state. That way you don't need to render 3 (or more) <ToggleContainer>. In <ToggleContainer> pass the selected tabs as props and render the selected tabs content in <ToggleContainer>.
import React, { useState } from "react";
import "./styles.css";
import { makeStyles } from "#material-ui/core/styles";
import Avatar from "#material-ui/core/Avatar";
import { deepOrange } from "#material-ui/core/colors";
import clsx from "clsx";
const useStyles = makeStyles((theme) => ({
root: {
display: "flex",
"& > *": {
margin: theme.spacing(1)
}
},
orange: {
color: theme.palette.getContrastText(deepOrange[500]),
backgroundColor: deepOrange[500],
border: "4px solid black"
},
info: {
margin: "10px"
},
wrapper: {
display: "flex"
},
contentWrapper: {
display: "flex",
flexDirection: "column"
},
elWrapper: {
opacity: 0,
"&.active": {
opacity: 1
}
}
}));
const ToggleItem = ({ onChange, id, styles, discription }) => {
const [toggleThisButton, setToggleThisButton] = useState(false);
const handleClick = (index) => {
onChange(discription, !toggleThisButton);
setToggleThisButton((prev) => !prev);
};
return (
<>
<Avatar
className={toggleThisButton ? styles.orange : ""}
onClick={() => handleClick(id)}
>
{id}
</Avatar>
{JSON.stringify(toggleThisButton)}
{/* {toggleThisButton && <div className={styles.info}>{discription}</div> } */}
</>
);
};
const ToggleContainer = ({ className, selected }) => {
return (
<div className={className}>
{selected.map((item, idx) => (
<div key={idx}>Content {item}</div>
))}
</div>
);
};
export default function App() {
const data = ["first", "second", "third"];
const classes = useStyles();
const [selected, setSelected] = useState([]);
// action : False -> Remove, True -> Add
const handleChange = (val, action) => {
let newVal = [];
if (action) {
// If toggle on, add content in selected state
newVal = [...selected, val];
} else {
// If toggle off, then remove content from selected state
newVal = selected.filter((v) => v !== val);
}
console.log(newVal);
setSelected(newVal);
};
return (
<>
<div className={classes.wrapper}>
{data.map((d, id) => {
return (
<div key={id}>
<ToggleItem
id={id}
styles={classes}
discription={d}
onChange={handleChange}
/>
</div>
);
})}
</div>
<div className={classes.contentWrapper}>
<ToggleContainer styles={classes} selected={selected} />
</div>
</>
);
}

Change only state of clicked card

How can I get the clicked card only to change its string from 'not captured' to 'captured'? Right now, all cards' strings say 'captured' even if I click on only one. I think the problem is that the captured state updates for all the cards and I can't get the captured state to update for the single clicked card. It's an onChange event and a checkbox.
import React, { useState, useEffect } from 'react'
import PokemonCard from '../components/PokemonCard';
const Pokedex = () => {
const [pokemons, setPokemons] = useState([]);
const [captured, setCaptured] = useState(false )
const URL = 'https://pokeapi.co/api/v2/pokemon/?limit=151';
const fetchingPokemons = async () => {
const res = await fetch(URL);
const data = await res.json();
// console.log(data)
setPokemons(data.results)
}
useEffect(() => {
fetchingPokemons()
}, [URL])
const toggleCaptured= (e, id) => {
console.log(id)
if(id && e) {
console.log('oh')
setCaptured(captured => !captured)
}
let capturedPkm = [];
let notCapturedPkm = [];
pokemons.forEach(i => {
if(captured === true) {
capturedPkm.push(pokemons[i])
} else {
notCapturedPkm.push(pokemons[i])
}
})
console.log('captured', capturedPkm, 'not captured', notCapturedPkm)
}
return (
<>
<div style={{display: 'flex', flexWrap: 'wrap', justifyContent: 'space-evenly'}}>
{pokemons ? pokemons.map((pokemon) => {
return (
<>
<div style={{ width: '235px' }} >
<PokemonCard
pokemon={pokemon}
name={pokemon.name}
url={pokemon.url}
key={pokemon.id}
captured={captured}
toggleCaptured={toggleCaptured}
/>
</div>
</>
)
}) : <h1>Loading...</h1>}
</div>
</>
)
}
export default Pokedex
import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import PokemonIcon from './PokemonIcon';
const PokemonCard = (props) => {
const { url, captured, toggleCaptured } = props
const URL = url
const [pokemonCard, setPokemonCard] = useState([])
const fetchingPokemonCard = async () => {
const res = await fetch(URL);
const data = await res.json();
//console.log(data)
setPokemonCard(data)
}
useEffect(() => {
fetchingPokemonCard()
}, [URL])
return (
<>
<div className='pokemon-card' style={{
height: '250px',
maxWidth: '250px',
margin: '1rem',
boxShadow: '5px 5px 5px 4px rgba(0, 0, 0, 0.3)',
cursor: 'pointer',
}} >
<Link
to={{ pathname: `/pokemon/${pokemonCard.id}` }}
state={{ pokemon: pokemonCard, captured }}
style={{ textDecoration: 'none', color: '#000000' }}>
<div
style={{ padding: '20px', display: 'flex', justifyContent: 'center', alignItems: 'center' }} >
<PokemonIcon img={pokemonCard.sprites?.['front_default']} />
</div>
</Link>
<div style={{ textAlign: 'center' }}>
<h1 >{pokemonCard.name}</h1>
<label >
<input
type='checkbox'
defaultChecked= {captured}
onChange={(e) => toggleCaptured(e.target.checked, pokemonCard.id)}
/>
<span style={{ marginLeft: 8, cursor: 'pointer' }}>
{captured === false ? 'Not captured!' : 'Captured!'}
{console.log(captured)}
</span>
</label>
</div>
</div>
<div>
</div>
</>
)
}
export default PokemonCard
Use an object as state:
const [captured, setCaptured] = useState({})
Set the toggle function:
const toggleCaptured = (checked, id) => {
const currentChecked = { ...captured }; // Create a shallow copy
console.log(id)
if (checked) {
currentChecked[id] = true;
} else {
delete currentChecked[id];
}
setCaptured(currentChecked); // Update the state
let capturedPkm = pokemons.filter(({id}) => currentChecked[id]);
let notCapturedPkm = pokemons.filter(({id}) => !currentChecked[id]);
console.log('captured', capturedPkm, 'not captured', notCapturedPkm)
}
The PokemonCard should look like this
<PokemonCard
pokemon={pokemon}
name={pokemon.name}
url={pokemon.url}
key={pokemon.id}
captured={captured[pokemon.id]} // Here
toggleCaptured={toggleCaptured}
/>

TypeError: Cannot read property 'clickNode' of undefined

I created these two functions inside my react component. The following code returns an error:
const UncontrolledDiagram = ({ sentence }) => {
// create diagrams schema
const [schema, { onChange, addNode, removeNode }] = useSchema(initialSchema);
const clickNode = () => {
console.log("Click event");
}
const BaseNode = ({ content }) => (
<div className='button' style={{ width: '70px', fontSize: '0.6rem', textAlign: 'center' }}>
<a onClick={this.clickNode}>
<div role="button">
{content}
</div>
</a>
</div>
);
How can I call clickNode from within BaseNode?
You no need to write 'this' inside the functional components, so your code should looks like this
const UncontrolledDiagram = ({ sentence }) => {
// create diagrams schema
const [schema, { onChange, addNode, removeNode }] = useSchema(initialSchema);
const clickNode = () => {
console.log("Click event");
}
const BaseNode = ({ content }) => (
<div className='button' style={{ width: '70px', fontSize: '0.6rem', textAlign: 'center' }}>
<a onClick={clickNode}>
<div role="button">
{content}
</div>
</a>
</div>
);

Why my props are undefined when sending to components?

I have a table in my project and I have an edit / view / add page that I can access from this table. My goal is to send the clicked data to the other component without any problem, but no matter how hard I try, I get an undefined error and the project is broken. I would be glad if you could help.
I am sharing my codes from parent to child component
Table page.
import React, { useState, useEffect, useCallback, useMemo } from "react";
import ManagementTable from '../components/ManagementTable'
import {
getApps,
updateStopRisk,
countLiveCountry,
updateAppShow,
deleteApp,
} from "../api/apiCalls";
import VisibilityIcon from "#material-ui/icons/Visibility";
import DeleteIcon from "#material-ui/icons/Delete";
import EditIcon from "#material-ui/icons/Edit";
import Switch from "#material-ui/core/Switch";
import DeleteModal from "../components/DeleteModal";
import { Link } from "react-router-dom";
const Management = () => {
const [apps, setApps] = useState([]);
const [modalVisible, setModalVisible] = useState(false);
const [currentApp, setCurrentApp] = useState("");
const [appID, setAppID] = useState(0);
const fetchData = useCallback(async () => {
const { data: appsResponse } = await getApps();
const countLiveCountries = await fetchLiveCountriesForApps(appsResponse);
setApps(
appsResponse.map((app, idx) => ({
...app,
countLiveCountry: countLiveCountries[idx],
}))
);
}, []);
useEffect(() => {
fetchData();
}, [fetchData]);
const fetchLiveCountriesForApps = async (appwLive) => {
const countLiveCountries = await Promise.all(
appwLive.map((app) => countLiveCountry(app.appID))
);
return countLiveCountries.map(({ data: liveCountries }) => liveCountries);
};
const removeApp = async () => {
await deleteApp(appID);
setModalVisible(false);
fetchData();
};
const onClickCancel = () => {
setModalVisible(false);
};
const columns = useMemo(() => [
{
Header: "Application Name",
accessor: "app_name",
},
{
Header: "Business Area",
accessor: "businessarea.businessarea_name",
},
{
Header: "Live Plants",
accessor: "countLiveCountry",
},
{
Header: "Line Stop Risk",
accessor: "app_stoprisk",
Cell: ({ row: { original } }) => {
const changeCheck = async (id) => {
await updateStopRisk(id);
fetchData();
};
return (
<input
checked={original.app_stoprisk}
onClick={() => {
changeCheck(original.appID);
}}
id="appStopRisk"
type="checkbox"
style={{ width: 18, height: 18, marginTop: 5 }}
/>
)
},
sortType: (a, b, id) => {
if (a.original[id] > b.original[id]) return -1;
if (b.original[id] > a.original[id]) return 1;
},
},
{
Header: "Actions",
Cell: ({ row: { original } }) => {
const changeTrack = async (id) => {
await updateAppShow(id);
fetchData();
};
return (
<>
<Link
className="btn btn-manage-link btn-sm col-2"
to={{
pathname: `/management/${original.app_name}`,
mode: "view",
id: original.appID
}}
>
<VisibilityIcon></VisibilityIcon>
</Link>
<Link
to={{
pathname: `/management/${original.app_name}`,
mode: "edit",
id: original.appID
}}
className="btn btn-manage-link btn-sm col-2"
>
<EditIcon></EditIcon>
</Link>
<button
onClick={() => {
setModalVisible(true);
setCurrentApp(original.app_name);
setAppID(original.appID);
}}
className="btn btn-manage-link btn-sm col-3"
>
<DeleteIcon></DeleteIcon>
</button>
<Switch
onClick={() => changeTrack(original.appID)}
checked={original.app_show}
className="col-3"
></Switch>
</>
)
},
},
],
[fetchData]
);
return (
<div className="container">
<h2 style={{ float: "left", font: "bold" }}>Management</h2>
<div style={{ float: "right" }}>
<Link className="btn btn-danger btn-sm" to={{ pathname: `/management/add`, mode: "add" }}>
Add New App
</Link>
<Link className="btn btn-danger btn-sm ml-3" exact to="/management/plants">
Plant Management
</Link>
</div>
<ManagementTable columns={columns} data={apps} />
<DeleteModal
message={<strong>{currentApp}</strong>}
variety="app"
onClickCancel={onClickCancel}
onClickOk={removeApp}
visible={modalVisible}
/>
</div>
);
};
export default Management;
The page where I transfer the props.
import React, { useState, useEffect } from "react";
import Accordion from "../components/Accordion";
import Details from '../components/Details'
import {
getByIdApps,
} from "../api/apiCalls";
const ApplicationManagement = (props) => {
const [appById, setAppById] = useState([]);
const { id } = props.location;
const [selectOption, setSelectOption] = useState('add')
useEffect(() => {
getData();
getMode();
}, [])
const getData = async () => {
console.log(props.location.id)
if (props.location.id) {
await getByIdApps(props.location.id).then((response) => setAppById(response.data))
console.log(appById)
console.log(props)
}
else {
setSelectOption('add')
}
}
const getMode = () => props.location.mode ? setSelectOption(props.location.mode) : setSelectOption('add')
const handleOptionChange = (event) => {
console.log(event.target.value)
setSelectOption(event.target.value)
}
return (
<>
<div style={{ margin: 20 }}>
<h1>
{appById.app_shortcode} - {appById.app_fullname}
</h1>
<div className="float-right mb-auto">
<label><input type="radio" value="view" checked={selectOption === 'view'} onChange={handleOptionChange} />View</label>
<label> <input type="radio" value="add" checked={selectOption === 'add'} onChange={handleOptionChange} />Add</label>
<label> <input type="radio" value="edit" checked={selectOption === 'edit'} onChange={handleOptionChange} />Edit</label>
</div>
<br></br>
<div style={{ marginLeft: 50, marginRight: 50 }} >
<Accordion
title={
<div style={{ width: 1350 }}>
<h3>Details</h3>
<hr style={{ backgroundColor: "#aaa" }}></hr>
</div>
}
content={
<Details appID={id} data = {appById}></Details>
}
/>
<Accordion title={
<div style={{ width: 1350 }}>
<h3>Links</h3>
<hr style={{ backgroundColor: "#aaa" }}></hr>
</div>
}></Accordion>
<Accordion title={
<div style={{ width: 1350 }}>
<h3>Factory Management</h3>
<hr style={{ backgroundColor: "#aaa" }}></hr>
</div>
}></Accordion>
<Accordion title={
<div style={{ width: 1350 }}>
<h3>Issues Management</h3>
<hr style={{ backgroundColor: "#aaa" }}></hr>
</div>
}></Accordion>
<Accordion title={
<div style={{ width: 1350 }}>
<h3>Middleware Management</h3>
<hr style={{ backgroundColor: "#aaa" }}></hr>
</div>
}></Accordion>
</div>)
{selectOption === 'add' ? (
<div>
Add Mode
</div>
) : selectOption === 'view' ? (<div>View Mode</div>) : (<div>eidt</div>)}
</div>
</>
);
};
export default ApplicationManagement;
and the section where the details are kept on the ApplicationManagement page (My code is quite long, I just share the problem part.)
import React, { useState, useEffect } from 'react'
import axios from "axios";
import {
getResponsibleTeams,
getBusinessAreas
} from '../api/apiCalls'
const Details = (props) => {
const [rTeams, setrTeams] = useState([]);
const [bAreas, setbAreas] = useState([]);
const { data } = props;
useEffect(() => {
async function fetchData() {
const getrTeams = await getResponsibleTeams();
const getbAreas = await getBusinessAreas();
axios.all([getrTeams, getbAreas]).then(
axios.spread((...allData) => {
const allrTeams = allData[0].data;
const allbAreas = allData[1].data;
setrTeams(allrTeams);
setbAreas(allbAreas);
})
);
}
fetchData();
}, []);
return (
<div>
<div
style={{
float: "left",
width: 1350,
height: 340,
}}
>
<div className="form-group">
<label style={{ float: "left" }} htmlFor="appFullName">
Frontend:{" "}
</label>
<input
id="appFullName"
type="text"
class="form-control"
placeholder="dsfdsdsf"
value={data.frontend.frontend_name} // error here
//onChange={handleInputChange}
name="appShortCode"
style={{ width: 400, marginLeft: 150 }}
/>
</div>
</div>
</div>
)
}
export default Details;
Later I realized that using asynchronous functions caused some problems. I came up with a solution to this and the problem was solved.
Error Code Here :
<Accordion
title={
<div style={{ width: 1350 }}>
<h3>Details</h3>
<hr style={{ backgroundColor: "#aaa" }}></hr>
</div>
}
content={
<Details appID={id} data = {appById}></Details>
}
/>
and the solution to the problem
{appById &&
<Accordion
title={
<div style={{ width: 1350 }}>
<h3>Details</h3>
<hr style={{ backgroundColor: "#aaa" }}></hr>
</div>
}
content={
<Details appID={id} data = {appById}></Details>
}
/>}

Resources