1684856942
Jotai scales from a simple useState replacement to an enterprise TypeScript application.
An atom represents a piece of state. All you need is to specify an initial value, which can be primitive values like strings and numbers, objects, and arrays. You can create as many primitive atoms as you want.
import { atom } from 'jotai'
const countAtom = atom(0)
const countryAtom = atom('Japan')
const citiesAtom = atom(['Tokyo', 'Kyoto', 'Osaka'])
const mangaAtom = atom({ 'Dragon Ball': 1984, 'One Piece': 1997, Naruto: 1999 })
It can be used like React.useState
:
import { useAtom } from 'jotai'
function Counter() {
const [count, setCount] = useAtom(countAtom)
return (
<h1>
{count}
<button onClick={() => setCount((c) => c + 1)}>one up</button>
...
A new read-only atom can be created from existing atoms by passing a read function as the first argument. get
allows you to fetch the contextual value of any atom.
const doubledCountAtom = atom((get) => get(countAtom) * 2)
function DoubleCounter() {
const [doubledCount] = useAtom(doubledCountAtom)
return <h2>{doubledCount}</h2>
}
You can combine multiple atoms to create a derived atom.
const count1 = atom(1)
const count2 = atom(2)
const count3 = atom(3)
const sum = atom((get) => get(count1) + get(count2) + get(count3))
Or if you like fp patterns ...
const atoms = [count1, count2, count3, ...otherAtoms]
const sum = atom((get) => atoms.map(get).reduce((acc, count) => acc + count))
You can make the read function an async function too.
const urlAtom = atom('https://json.host.com')
const fetchUrlAtom = atom(async (get) => {
const response = await fetch(get(urlAtom))
return await response.json()
})
function Status() {
// Re-renders the component after urlAtom is changed and the async function above concludes
const [json] = useAtom(fetchUrlAtom)
...
Specify a write function at the second argument. get
will return the current value of an atom. set
will update the value of an atom.
const decrementCountAtom = atom(
(get) => get(countAtom),
(get, set, _arg) => set(countAtom, get(countAtom) - 1)
)
function Counter() {
const [count, decrement] = useAtom(decrementCountAtom)
return (
<h1>
{count}
<button onClick={decrement}>Decrease</button>
...
Just do not define a read function.
const multiplyCountAtom = atom(null, (get, set, by) =>
set(countAtom, get(countAtom) * by)
)
function Controls() {
const [, multiply] = useAtom(multiplyCountAtom)
return <button onClick={() => multiply(3)}>triple</button>
}
Just make the write function an async function and call set
when you're ready.
const fetchCountAtom = atom(
(get) => get(countAtom),
async (_get, set, url) => {
const response = await fetch(url)
set(countAtom, (await response.json()).count)
}
)
function Controls() {
const [count, compute] = useAtom(fetchCountAtom)
return (
<button onClick={() => compute('http://count.host.com')}>compute</button>
...
visit jotai.org or npm i jotai
Author: pmndrs
Source Code: https://github.com/pmndrs/jotai
License: MIT license
1683818727
flutter_prevent_nap
A Flutter plugin to prevent app into app nap state.
final preventNap = FlutterPreventNap();
final key = await preventNap.beginActivity("");
// do something important
preventNap.endActivity(key);
Run this command:
With Flutter:
$ flutter pub add flutter_prevent_nap
This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get
):
dependencies:
flutter_prevent_nap: ^0.0.3
Alternatively, your editor might support flutter pub get
. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:flutter_prevent_nap/flutter_prevent_nap.dart';
import 'package:flutter/material.dart';
import 'package:flutter_prevent_nap/flutter_prevent_nap.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Center(
child: ElevatedButton(
onPressed: () async {
final preventNap = FlutterPreventNap();
final key = await preventNap.beginActivity("");
// do something important
preventNap.endActivity(key);
},
child: const Text("Execute an important task!"),
),
),
),
);
}
}
Download Details:
Author: farmer00317558
Source Code: https://github.com/farmer00317558/flutter_prevent_nap
1683216496
The flow state will be the state which drives the flow. Each time this state changes, a new navigation stack will be generated based on the new flow state.
class Profile {
const Profile({this.name, this.age, this.weight});
final String? name;
final int? age;
final int? weight;
Profile copyWith({String? name, int? age, int? weight}) {
return Profile(
name: name ?? this.name,
age: age ?? this.age,
weight: weight ?? this.weight,
);
}
}
FlowBuilder
is a widget which builds a navigation stack in response to changes in the flow state. onGeneratePages
will be invoked for each state change and must return the new navigation stack as a list of pages.
FlowBuilder<Profile>(
state: const Profile(),
onGeneratePages: (profile, pages) {
return [
MaterialPage(child: NameForm()),
if (profile.name != null) MaterialPage(child: AgeForm()),
];
},
);
The state of the flow can be updated via context.flow<T>().update
.
class NameForm extends StatefulWidget {
@override
_NameFormState createState() => _NameFormState();
}
class _NameFormState extends State<NameForm> {
var _name = '';
void _continuePressed() {
context.flow<Profile>().update((profile) => profile.copyWith(name: _name));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Name')),
body: Center(
child: Column(
children: <Widget>[
TextField(
onChanged: (value) => setState(() => _name = value),
decoration: InputDecoration(
labelText: 'Name',
hintText: 'John Doe',
),
),
RaisedButton(
child: const Text('Continue'),
onPressed: _name.isNotEmpty ? _continuePressed : null,
)
],
),
),
);
}
}
The flow can be completed via context.flow<T>().complete
.
class AgeForm extends StatefulWidget {
@override
_AgeFormState createState() => _AgeFormState();
}
class _AgeFormState extends State<AgeForm> {
int? _age;
void _continuePressed() {
context
.flow<Profile>()
.complete((profile) => profile.copyWith(age: _age));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Age')),
body: Center(
child: Column(
children: <Widget>[
TextField(
onChanged: (value) => setState(() => _age = int.parse(value)),
decoration: InputDecoration(
labelText: 'Age',
hintText: '42',
),
keyboardType: TextInputType.number,
),
RaisedButton(
child: const Text('Continue'),
onPressed: _age != null ? _continuePressed : null,
)
],
),
),
);
}
}
A FlowBuilder
can also be created with a custom FlowController
in cases where the flow can be manipulated outside of the sub-tree.
class MyFlow extends StatefulWidget {
@override
State<MyFlow> createState() => _MyFlowState();
}
class _MyFlowState extends State<MyFlow> {
late FlowController<Profile> _controller;
@override
void initState() {
super.initState();
_controller = FlowController(const Profile());
}
@override
Widget build(BuildContext context) {
return FlowBuilder(
controller: _controller,
onGeneratePages: ...,
);
}
@override dispose() {
_controller.dispose();
super.dispose();
}
}
Run this command:
With Flutter:
$ flutter pub add flow_builder
This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get
):
dependencies:
flow_builder: ^0.0.9
Alternatively, your editor might support flutter pub get
. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:flow_builder/flow_builder.dart';
import 'package:example/authentication_flow/authentication_flow.dart';
import 'package:example/location_flow/location_flow.dart';
import 'package:example/onboarding_flow/onboarding_flow.dart';
import 'package:example/profile_flow/profile_flow.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
void main() => runApp(MyApp(locationRepository: LocationRepository()));
class MyApp extends StatelessWidget {
MyApp({required LocationRepository locationRepository})
: _locationRepository = locationRepository;
final LocationRepository _locationRepository;
@override
Widget build(BuildContext context) {
return RepositoryProvider.value(
value: _locationRepository,
child: MaterialApp(home: Home()),
);
}
}
class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Home')),
body: Builder(
builder: (context) {
return ListView(
children: [
ListTile(
leading: const Icon(Icons.help_outline),
title: const Text('Onboarding Flow'),
trailing: const Icon(Icons.chevron_right),
onTap: () async {
await Navigator.of(context).push(OnboardingFlow.route());
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(
const SnackBar(
content: Text('Onboarding Flow Complete!'),
),
);
},
),
ListTile(
leading: const Icon(Icons.person_outline),
title: const Text('Profile Flow'),
trailing: const Icon(Icons.chevron_right),
onTap: () async {
final profile = await Navigator.of(context).push(
ProfileFlow.route(),
);
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(
SnackBar(
content: Text('Profile Flow Complete!\n$profile'),
),
);
},
),
ListTile(
leading: const Icon(Icons.location_city),
title: const Text('Location Flow'),
trailing: const Icon(Icons.chevron_right),
onTap: () async {
final location = await Navigator.of(context).push(
LocationFlow.route(),
);
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(
SnackBar(
content: Text('Location Flow Complete!\n$location'),
),
);
},
),
ListTile(
leading: const Icon(Icons.security_rounded),
title: const Text('Authentication Flow'),
trailing: const Icon(Icons.chevron_right),
onTap: () async {
await Navigator.of(context).push<AuthenticationState>(
AuthenticationFlow.route(),
);
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(
const SnackBar(
content: Text('Authentication Flow Complete!'),
),
);
},
),
],
);
},
),
);
}
}
Download Details:
Author: felangel.dev
Source Code: https://github.com/felangel/flow_builder
1681913589
Dynamic-dependent dropdowns are very helpful to restrict user selection. You may have already seen this on the registration or profile page of the website where you need to select country, state, and city. Options in the dropdown update every time when a country or state dropdown selection gets changed.
If your tables are properly interlinked in the database and want to list their data on the dropdown then you can make them dynamically dependent on one another and load their data using AJAX.
In this tutorial, I show how you can auto populate dropdown using jQuery AJAX with MySQL database data in CakePHP 4.
In the example, I am using 3 tables –
countries (Store countries name) –
CREATE TABLE `countries` (
`id` int(10) UNSIGNED NOT NULL,
`name` varchar(80) NOT NULL
);
ALTER TABLE `countries`
ADD PRIMARY KEY (`id`);
ALTER TABLE `countries`
MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT;
states (Store country states names) –
Added foreign key on country_id
field.
CREATE TABLE `states` (
`id` int(10) UNSIGNED NOT NULL,
`country_id` int(10) UNSIGNED NOT NULL,
`name` varchar(80) NOT NULL
);
ALTER TABLE `states`
ADD PRIMARY KEY (`id`),
ADD KEY `states_country_id_foreign` (`country_id`);
ALTER TABLE `states`
MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT;
cities (Store state cities name) –
Added foreign key on state_id
field.
CREATE TABLE `cities` (
`id` int(10) UNSIGNED NOT NULL,
`state_id` int(10) UNSIGNED NOT NULL,
`name` varchar(80) NOT NULL
);
ALTER TABLE `cities`
ADD PRIMARY KEY (`id`),
ADD KEY `cities_state_id_foreign` (`state_id`);
ALTER TABLE `cities`
MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT;
config/app_local.php
file.Datasources
default
.'Datasources' => [
'default' => [
'host' => '127.0.0.1',
/*
* CakePHP will use the default DB port based on the driver selected
* MySQL on MAMP uses port 8889, MAMP users will want to uncomment
* the following line and set the port accordingly
*/
//'port' => 'non_standard_port_number',
'username' => 'root',
'password' => 'root',
'database' => 'cakephp4',
/*
* If not using the default 'public' schema with the PostgreSQL driver
* set it here.
*/
//'schema' => 'myapp',
/*
* You can use a DSN string to set the entire configuration
*/
'url' => env('DATABASE_URL', null),
],
/*
* The test connection is used during the test suite.
*/
'test' => [
'host' => 'localhost',
//'port' => 'non_standard_port_number',
'username' => 'my_app',
'password' => 'secret',
'database' => 'test_myapp',
//'schema' => 'myapp',
'url' => env('DATABASE_TEST_URL', 'sqlite://127.0.0.1/tests.sqlite'),
],
],
Create 3 models –
Countries Model –
bin/cake bake model Countries
This will create 2 files –
<?php
declare(strict_types=1);
namespace App\Model\Entity;
use Cake\ORM\Entity;
class Country extends Entity
{
protected $_accessible = [
'name' => true,
'states' => true,
];
}
<?php
declare(strict_types=1);
namespace App\Model\Table;
use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class CountriesTable extends Table
{
public function initialize(array $config): void
{
parent::initialize($config);
$this->setTable('countries');
$this->setDisplayField('name');
$this->setPrimaryKey('id');
$this->hasMany('States', [
'foreignKey' => 'country_id',
]);
}
public function validationDefault(Validator $validator): Validator
{
$validator
->scalar('name')
->maxLength('name', 255)
->requirePresence('name', 'create')
->notEmptyString('name');
return $validator;
}
}
States Model –
bin/cake bake model States
This will create 2 files –
<?php
declare(strict_types=1);
namespace App\Model\Entity;
use Cake\ORM\Entity;
class State extends Entity
{
protected $_accessible = [
'country_id' => true,
'name' => true,
'country' => true,
'cities' => true,
];
}
<?php
declare(strict_types=1);
namespace App\Model\Table;
use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class StatesTable extends Table
{
public function initialize(array $config): void
{
parent::initialize($config);
$this->setTable('states');
$this->setDisplayField('name');
$this->setPrimaryKey('id');
$this->belongsTo('Countries', [
'foreignKey' => 'country_id',
'joinType' => 'INNER',
]);
$this->hasMany('Cities', [
'foreignKey' => 'state_id',
]);
}
public function validationDefault(Validator $validator): Validator
{
$validator
->notEmptyString('country_id');
$validator
->scalar('name')
->maxLength('name', 255)
->requirePresence('name', 'create')
->notEmptyString('name');
return $validator;
}
public function buildRules(RulesChecker $rules): RulesChecker
{
$rules->add($rules->existsIn('country_id', 'Countries'), ['errorField' => 'country_id']);
return $rules;
}
}
Cities Model –
This will create 2 files –
<?php
declare(strict_types=1);
namespace App\Model\Entity;
use Cake\ORM\Entity;
class City extends Entity
{
protected $_accessible = [
'state_id' => true,
'name' => true,
'state' => true,
];
}
<?php
declare(strict_types=1);
namespace App\Model\Table;
use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class CitiesTable extends Table
{
public function initialize(array $config): void
{
parent::initialize($config);
$this->setTable('cities');
$this->setDisplayField('name');
$this->setPrimaryKey('id');
$this->belongsTo('States', [
'foreignKey' => 'state_id',
'joinType' => 'INNER',
]);
}
public function validationDefault(Validator $validator): Validator
{
$validator
->notEmptyString('state_id');
$validator
->scalar('name')
->maxLength('name', 255)
->requirePresence('name', 'create')
->notEmptyString('name');
return $validator;
}
public function buildRules(RulesChecker $rules): RulesChecker
{
$rules->add($rules->existsIn('state_id', 'States'), ['errorField' => 'state_id']);
return $rules;
}
}
AutopopulateController.php
file in src/Controller/
folder.AutopopulateController
Class that extends AppController
.Create 3 method –
countries
table. Loop on the fetched records and store them in $country_arr
Array. Using $this->set()
pass $country_arr
Array to template.Read POST country_id
and assign it to the variable. Fetch all records from the states
table where country_id = $country_id
.
Loop on the fetch records and store state id and name in $data_arr
Array. Return $data_arr
Array in JSON format.
Read POST state_id
and assign it to the variable. Fetch all records from the cities
table where state_id = $state_id
.
Loop on the fetch records and store city id and name in $data_arr
Array. Return $data_arr
Array in JSON format.
Completed Code
<?php
declare(strict_types=1);
namespace App\Controller;
class AutopopulateController extends AppController
{
public function index(){
## Fetch all countries
$countries = $this->getTableLocator()->get('Countries');
$query = $countries->find('all')->order(['name' => 'ASC']);
$countriesList = $query->toArray();
$country_arr = array();
foreach($countriesList as $country){
$country_arr[$country['id']] = $country['name'];
}
$this->set(compact('country_arr'));
}
// Get country states
public function getCountryStates(){
// POST value
$country_id = $this->request->getData()['country_id'];
## Fetch all states of country_id
$states = $this->getTableLocator()->get('States');
$query = $states->find('all')
->where(['country_id' => $country_id])
->order(['name' => 'ASC']);
$statesList = $query->toArray();
$data_arr = array();
foreach($statesList as $state){
$data_arr[] = array(
'id' => $state['id'],
'name' => $state['name']
);
}
echo json_encode($data_arr);
die;
}
// Get state cities
public function getStateCities(){
// POST value
$state_id = $this->request->getData()['state_id'];
## Fetch all cities of state_id
$cities = $this->getTableLocator()->get('Cities');
$query = $cities->find('all')
->where(['state_id' => $state_id])
->order(['name' => 'ASC']);
$citiesList = $query->toArray();
$data_arr = array();
foreach($citiesList as $city){
$data_arr[] = array(
'id' => $city['id'],
'name' => $city['name']
);
}
echo json_encode($data_arr);
die;
}
}
I am including jQuery and CSRF token on templates/layout/default.php
file.
Stored CSRF token in <meta >
tag –
<?= $this->Html->meta('csrfToken', $this->request->getAttribute('csrfToken')); ?>
and jQuery in <head >
section –
<?= $this->Html->script('https://code.jquery.com/jquery.min.js'); ?>
Completed Code
<?php
$cakeDescription = 'CakePHP: the rapid development php framework';
?>
<!DOCTYPE html>
<html>
<head>
<?= $this->Html->charset() ?>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- CSRF Token -->
<?= $this->Html->meta('csrfToken', $this->request->getAttribute('csrfToken')); ?>
<title>
<?= $cakeDescription ?>:
<?= $this->fetch('title') ?>
</title>
<?= $this->Html->meta('icon') ?>
<link href="https://fonts.googleapis.com/css?family=Raleway:400,700" rel="stylesheet">
<?= $this->Html->css(['normalize.min', 'milligram.min', 'cake']) ?>
<!-- jQuery -->
<?= $this->Html->script('https://code.jquery.com/jquery.min.js'); ?>
<?= $this->fetch('meta') ?>
<?= $this->fetch('css') ?>
<?= $this->fetch('script') ?>
</head>
<body>
<nav class="top-nav">
<div class="top-nav-title">
<a href="<?= $this->Url->build('/') ?>"><span>Cake</span>PHP</a>
</div>
<div class="top-nav-links">
<a target="_blank" rel="noopener" href="https://book.cakephp.org/4/">Documentation</a>
<a target="_blank" rel="noopener" href="https://api.cakephp.org/">API</a>
</div>
</nav>
<main class="main">
<div class="container">
<?= $this->Flash->render() ?>
<?= $this->fetch('content') ?>
</div>
</main>
<footer>
</footer>
</body>
</html>
Create Autopopulate
folder in templates/
location. In the Autopopulate
folder create index.php
file – templates/Autopopulate/index.php
.
Create 3 <select >
elements –
jQuery
Read CSRF token from the <meta >
tag and assign it to csrfToken
variable.
Country Change –
Define change
event on #country_id
. Read selected country_id
and empty the state and city dropdown. If country_id
is not empty then send AJAX POST request to <?= $this->Url->build(['controller' => 'Autopopulate','action' => 'getCountryStates']) ?>
.
Here, pass country_id – {country_id: country_id}
as data, set dataType: 'json'
, pass CSRF token using header.
On successful callback loop on the response
and add a new <option >
in state dropdown.
State Change –
Define change
event on #state_id
. Read selected state_id
and empty the city dropdown. If state_id
is not empty then send AJAX POST request to <?= $this->Url->build(['controller' => 'Autopopulate','action' => 'getStateCities']) ?>
.
Here, pass state_id – {state_id: state_id}
as data, set dataType: 'json'
, pass CSRF token using header.
On successful callback loop on the response
to add <option >
in city dropdown.
Completed Code
<div class="row">
<div class="col-6">
<!-- Country -->
<div id="input">
<label for='country_id'>Country</label>
<?php
echo $this->Form->select(
'country_id',
$country_arr,
[
'id' => 'country_id',
'empty' => '-- Select Country --'
]
);
?>
</div>
<!-- State -->
<div id="input">
<label for='state_id'>State</label>
<?php
echo $this->Form->select(
'state_id',
[],
[
'id' => 'state_id',
'empty' => '-- Select State --'
]
);
?>
</div>
<!-- City -->
<div id="input">
<label for='city_id'>City</label>
<?php
echo $this->Form->select(
'city_id',
[],
[
'id' => 'city_id',
'empty' => '-- Select City --'
]
);
?>
</div>
</div>
</div>
<!-- Script -->
<script type="text/javascript">
// Read CSRF Token
var csrfToken = $('meta[name="csrfToken"]').attr('content');
$(document).ready(function(){
// Country change
$('#country_id').change(function(){
var country_id = $('#country_id').val();
// Empty state and city dropdown
$('#state_id').find('option').not(':first').remove();
$('#city_id').find('option').not(':first').remove();
if(country_id != ''){
// AJAX request
$.ajax({
url: "<?= $this->Url->build(['controller' => 'Autopopulate','action' => 'getCountryStates']) ?>",
type: 'post',
data: {country_id: country_id},
dataType: 'json',
headers:{
'X-CSRF-Token': csrfToken
},
success: function(response){
var len = response.length;
// Add response data to state dropdown
for( var i = 0; i<len; i++){
var id = response[i]['id'];
var name = response[i]['name'];
$("#state_id").append("<option value='"+id+"'>"+name+"</option>");
}
},
});
}
});
// State change
$('#state_id').change(function(){
var state_id = $('#state_id').val();
// Empty city dropdown
$('#city_id').find('option').not(':first').remove();
if(state_id != ''){
// AJAX request
$.ajax({
url: "<?= $this->Url->build(['controller' => 'Autopopulate','action' => 'getStateCities']) ?>",
type: 'post',
data: {state_id: state_id},
dataType: 'json',
headers:{
'X-CSRF-Token': csrfToken
},
success: function(response){
var len = response.length;
// Add response data to city dropdown
for( var i = 0; i<len; i++){
var id = response[i]['id'];
var name = response[i]['name'];
$("#city_id").append("<option value='"+id+"'>"+name+"</option>");
}
},
});
}
});
});
</script>
In the example, I returned a JSON response from the server and looped on it to add a new item to the dropdown, but you can also return an HTML response and directly append it to the dropdown.
After implementing this on your project if data is not loading in the dropdown then debug it using the browser console and network tab.
You can also checkout this tutorial if you want to know jQuery UI autocomplete using jQuery AJAX in CakePHP 4.
Original article source at: https://makitweb.com/
1680537460
phone_state_background
A flutter plugin to handle Phone Call State and execute a Dart callback in background.
This plugin only supported in android platform.
Corrently IOS is not supported. Unfortunately I'm not familiar with IOS development neither with Switf/ObjC languages, so if you wish to help any PR will be welcome.
Add the following permissions to your AndroidManifest.xml
file:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
Also, apparently it is necessary to register the broadcast receiver manually, otherwise an error will be throw saying that our receiver does not exist inside the app:
<receiver android:name="me.sodipto.phone_state_background.PhoneStateBackgroundServiceReceiver"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE" />
</intent-filter>
</receiver>
All you need to start using our package after installing it, is defining a callback which must be a top level function or static function, that will be called by our plugin when any incoming or outgoing call events are detected.
void phoneStateBackgroundCallbackHandler(PhoneStateBackgroundEvent event, String number, int duration)
This callback handler must accept 3 arguments:
phoneStateBackgroundEvent: The event type detect by our plugin in background.
number: The incoming/outgoin number that is triggering the phone state call.
duration: An integer that represents the duration of the call in seconds.
The PhoneStateBackgroundEvent
is an enum
with four possible values:
Event Value | Description |
---|---|
incomingstart | Indicates an incoming call. |
incomingmissed | Indicates an incoming call missed. |
incomingreceived | Indicates an incoming call received. |
incomingend | Indicates an incoming call end. |
outgoingstart | Indicates an outgoing call start. |
outgoingend | Indicates an outgoing call end. |
Since all this process happens in background in a Dart Isolate, there's no guarantee that the current OS will call the registered callback as soon as an event is triggered or that the callback will ever be called at all, each OS handle background services with different policies. Make sure to ask user permission before calling the PhoneStateBackground.initialize
method of our plugin. Check the example to see a simple implementation of it.
Run this command:
With Flutter:
$ flutter pub add phone_state_background
This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get
):
dependencies:
phone_state_background: ^0.0.2
Alternatively, your editor might support flutter pub get
. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:phone_state_background/phone_state_background.dart';
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:phone_state_background/phone_state_background.dart';
/// Defines a callback that will handle all background incoming events
Future<void> phoneStateBackgroundCallbackHandler(
PhoneStateBackgroundEvent event,
String number,
int duration,
) async {
switch (event) {
case PhoneStateBackgroundEvent.incomingstart:
print('Incoming call start, number: $number, duration: $duration s');
break;
case PhoneStateBackgroundEvent.incomingmissed:
print('Incoming call missed, number: $number, duration: $duration s');
break;
case PhoneStateBackgroundEvent.incomingreceived:
print('Incoming call received, number: $number, duration: $duration s');
break;
case PhoneStateBackgroundEvent.incomingend:
print('Incoming call ended, number: $number, duration $duration s');
break;
case PhoneStateBackgroundEvent.outgoingstart:
print('Ougoing call start, number: $number, duration: $duration s');
break;
case PhoneStateBackgroundEvent.outgoingend:
print('Ougoing call ended, number: $number, duration: $duration s');
break;
}
}
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Phone State Background',
theme: ThemeData(
primarySwatch: Colors.blueGrey,
),
home: const MyHomePage(title: 'Phone State Background'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool? hasPermission;
@override
void initState() {
super.initState();
_hasPermission();
}
Future<void> _hasPermission() async {
final permission = await PhoneStateBackground.checkPermission();
print('Permission $permission');
setState(() => hasPermission = permission);
}
Future<void> _requestPermission() async {
await PhoneStateBackground.requestPermissions();
await _hasPermission();
}
Future<void> _stop() async {
await PhoneStateBackground.stopPhoneStateBackground();
}
Future<void> _init() async {
if (hasPermission != true) return;
await PhoneStateBackground.initialize(phoneStateBackgroundCallbackHandler);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Has Permission: $hasPermission',
style: TextStyle(
fontSize: 16,
color: hasPermission! ? Colors.green : Colors.red),
),
const SizedBox(
height: 20,
),
SizedBox(
width: 180,
child: ElevatedButton(
onPressed: () => _requestPermission(),
child: const Text('Check Permission'),
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 20),
child: SizedBox(
width: 180,
child: ElevatedButton(
onPressed: () => _init(),
style: ElevatedButton.styleFrom(
primary: Colors.green, // Background color
),
child: const Text('Start Listener'),
),
),
),
SizedBox(
width: 180,
child: ElevatedButton(
onPressed: () => _stop(),
style: ElevatedButton.styleFrom(
primary: Colors.red, // Background color
),
child: const Text('Stop Listener'),
),
),
],
),
),
);
}
}
Download Details:
Author: sodipto
Source Code: https://github.com/sodipto/flutter-phone-state-background
1679462221
BlockSuite (pronounced "block sweet") is the open-source editor project behind AFFiNE. It provides an out-of-the-box block-based editor built on top of a framework designed for general-purpose collaborative applications. This monorepo maintains both the editor and the underlying framework.
⚠️ This project is under heavy development and is in a stage of rapid evolution. Stay tuned or see our roadmap here!
BlockSuite works very differently than traditional rich text frameworks:
contenteditable
monolith.BlockSuite is not intended to be yet another plugin-based rich text editing framework. Instead, it encourages building various collaborative applications directly through whatever UI framework you're comfortable with. To this end, we will try to open-source more foundational modules as reusable packages for this in the BlockSuite project.
Although BlockSuite is still in its early stages, you can already use the @blocksuite/editor
package, the collaborative editor used in AFFiNE Alpha. Note that this editor is also a web component and is completely framework-independent!
The @blocksuite/editor
package contains the editor built into AFFiNE. Its nightly
versions are released daily based on the master branch, and they are always tested on CI. This means that the nightly
versions can already be used in real-world projects like AFFiNE at any time:
pnpm i @blocksuite/editor@nightly
If you want to easily reuse most of the rich-text editing features, you can use the SimpleAffineEditor
web component directly (code example here):
import { SimpleAffineEditor } from '@blocksuite/editor';
import '@blocksuite/editor/themes/affine.css';
const editor = new SimpleAffineEditor();
document.body.appendChild(editor);
Or equivalently, you can also use the declarative style:
<body>
<simple-affine-editor></simple-affine-editor>
<script type="module">
import '@blocksuite/editor';
import '@blocksuite/editor/themes/affine.css';
</script>
</body>
👉 Try SimpleAffineEditor
online
However, the SimpleAffineEditor
here is just a thin wrapper with dozens of lines that doesn't enable the opt-in collaboration and data persistence features. If you are going to support more complicated real-world use cases (e.g., with customized block models and configured data sources), this will involve the use of these three following core packages:
packages/store
package is a data store built for general-purpose state management.packages/blocks
package holds the default BlockSuite editable blocks.packages/editor
package ships a complete BlockSuite-based editor.pnpm i \
@blocksuite/store@nightly \
@blocksuite/blocks@nightly \
@blocksuite/editor@nightly
And here is a minimal collaboration-ready editor showing how these underlying BlockSuite packages are composed together:
🚧 Here we will work with the concepts of
Workspace
,Page
,Block
andSlot
. These are the primitives for building a block-based collaborative application. We are preparing a comprehensive documentation about their usage!
import '@blocksuite/blocks';
import { Workspace, Page } from '@blocksuite/store';
import { AffineSchemas } from '@blocksuite/blocks/models';
import { EditorContainer } from '@blocksuite/editor';
function main() {
// Create a workspace with one default page
const workspace = new Workspace({ id: 'test' }).register(AffineSchemas);
const page = workspace.createPage('page0');
// Create default blocks in the page
const pageBlockId = page.addBlock('affine:page');
const frameId = page.addBlock('affine:frame', {}, pageBlockId);
page.addBlock('affine:paragraph', {}, frameId);
// Init editor with the page store
const editor = new EditorContainer();
editor.page = page;
document.body.appendChild(editor);
}
main();
For React developers, check out the @blocksuite/react
doc for React components and hooks support.
@blocksuite/editor
)For more detailed planning and progress, please checkout our GitHub project.
Icons above correspond to the following meanings:
See BUILDING.md for instructions on how to build BlockSuite from source code.
BlockSuite accepts pull requests on GitHub. Before you start contributing, please make sure you have read and accepted our Contributor License Agreement. To indicate your agreement, simply edit this file and submit a pull request.
Author: toeverything
Source Code: https://github.com/toeverything/blocksuite
License: MPL-2.0 license
#react #editor #components #block #state #management #webcomponents
1679182500
200 bytes to never think about React state management libraries ever again
But, the most important question: Is this better than Redux? Well...
So you decide.
npm install --save unstated-next
import React, { useState } from "react"
import { createContainer } from "unstated-next"
import { render } from "react-dom"
function useCounter(initialState = 0) {
let [count, setCount] = useState(initialState)
let decrement = () => setCount(count - 1)
let increment = () => setCount(count + 1)
return { count, decrement, increment }
}
let Counter = createContainer(useCounter)
function CounterDisplay() {
let counter = Counter.useContainer()
return (
<div>
<button onClick={counter.decrement}>-</button>
<span>{counter.count}</span>
<button onClick={counter.increment}>+</button>
</div>
)
}
function App() {
return (
<Counter.Provider>
<CounterDisplay />
<Counter.Provider initialState={2}>
<div>
<div>
<CounterDisplay />
</div>
</div>
</Counter.Provider>
</Counter.Provider>
)
}
render(<App />, document.getElementById("root"))
createContainer(useHook)
import { createContainer } from "unstated-next"
function useCustomHook() {
let [value, setValue] = useState()
let onChange = e => setValue(e.currentTarget.value)
return { value, onChange }
}
let Container = createContainer(useCustomHook)
// Container === { Provider, useContainer }
<Container.Provider>
function ParentComponent() {
return (
<Container.Provider>
<ChildComponent />
</Container.Provider>
)
}
<Container.Provider initialState>
function useCustomHook(initialState = "") {
let [value, setValue] = useState(initialState)
// ...
}
function ParentComponent() {
return (
<Container.Provider initialState={"value"}>
<ChildComponent />
</Container.Provider>
)
}
Container.useContainer()
function ChildComponent() {
let input = Container.useContainer()
return <input value={input.value} onChange={input.onChange} />
}
useContainer(Container)
import { useContainer } from "unstated-next"
function ChildComponent() {
let input = useContainer(Container)
return <input value={input.value} onChange={input.onChange} />
}
If you've never used React Hooks before, I recommend pausing and going to read through the excellent docs on the React site.
So with hooks you might create a component like this:
function CounterDisplay() {
let [count, setCount] = useState(0)
let decrement = () => setCount(count - 1)
let increment = () => setCount(count + 1)
return (
<div>
<button onClick={decrement}>-</button>
<p>You clicked {count} times</p>
<button onClick={increment}>+</button>
</div>
)
}
Then if you want to share the logic behind the component, you could pull it out into a custom hook:
function useCounter() {
let [count, setCount] = useState(0)
let decrement = () => setCount(count - 1)
let increment = () => setCount(count + 1)
return { count, decrement, increment }
}
function CounterDisplay() {
let counter = useCounter()
return (
<div>
<button onClick={counter.decrement}>-</button>
<p>You clicked {counter.count} times</p>
<button onClick={counter.increment}>+</button>
</div>
)
}
But what if you want to share the state in addition to the logic, what do you do?
This is where context comes into play:
function useCounter() {
let [count, setCount] = useState(0)
let decrement = () => setCount(count - 1)
let increment = () => setCount(count + 1)
return { count, decrement, increment }
}
let Counter = createContext(null)
function CounterDisplay() {
let counter = useContext(Counter)
return (
<div>
<button onClick={counter.decrement}>-</button>
<p>You clicked {counter.count} times</p>
<button onClick={counter.increment}>+</button>
</div>
)
}
function App() {
let counter = useCounter()
return (
<Counter.Provider value={counter}>
<CounterDisplay />
<CounterDisplay />
</Counter.Provider>
)
}
This is great, it's perfect, more people should write code like this.
But sometimes we all need a little bit more structure and intentional API design in order to get it consistently right.
By introducing the createContainer()
function, you can think about your custom hooks as "containers" and have an API that's clear and prevents you from using it wrong.
import { createContainer } from "unstated-next"
function useCounter() {
let [count, setCount] = useState(0)
let decrement = () => setCount(count - 1)
let increment = () => setCount(count + 1)
return { count, decrement, increment }
}
let Counter = createContainer(useCounter)
function CounterDisplay() {
let counter = Counter.useContainer()
return (
<div>
<button onClick={counter.decrement}>-</button>
<p>You clicked {counter.count} times</p>
<button onClick={counter.increment}>+</button>
</div>
)
}
function App() {
return (
<Counter.Provider>
<CounterDisplay />
<CounterDisplay />
</Counter.Provider>
)
}
Here's the diff of that change:
- import { createContext, useContext } from "react"
+ import { createContainer } from "unstated-next"
function useCounter() {
...
}
- let Counter = createContext(null)
+ let Counter = createContainer(useCounter)
function CounterDisplay() {
- let counter = useContext(Counter)
+ let counter = Counter.useContainer()
return (
<div>
...
</div>
)
}
function App() {
- let counter = useCounter()
return (
- <Counter.Provider value={counter}>
+ <Counter.Provider>
<CounterDisplay />
<CounterDisplay />
</Counter.Provider>
)
}
If you're using TypeScript (which I encourage you to learn more about if you are not), this also has the benefit of making TypeScript's built-in inference work better. As long as your custom hook is typed, then everything else will just work.
Because we're just working with custom React hooks, we can compose containers inside of other hooks.
function useCounter() {
let [count, setCount] = useState(0)
let decrement = () => setCount(count - 1)
let increment = () => setCount(count + 1)
return { count, decrement, increment, setCount }
}
let Counter = createContainer(useCounter)
function useResettableCounter() {
let counter = Counter.useContainer()
let reset = () => counter.setCount(0)
return { ...counter, reset }
}
This can be useful for keeping your containers small and focused. Which can be important if you want to code split the logic in your containers: Just move them to their own hooks and keep just the state in containers.
function useCount() {
return useState(0)
}
let Count = createContainer(useCount)
function useCounter() {
let [count, setCount] = Count.useContainer()
let decrement = () => setCount(count - 1)
let increment = () => setCount(count + 1)
let reset = () => setCount(0)
return { count, decrement, increment, reset }
}
There's no "optimizing" unstated-next
to be done, all of the optimizations you might do would be standard React optimizations.
Before:
function CounterDisplay() {
let counter = Counter.useContainer()
return (
<div>
<button onClick={counter.decrement}>-</button>
<p>You clicked {counter.count} times</p>
<button onClick={counter.increment}>+</button>
<div>
<div>
<div>
<div>SUPER EXPENSIVE RENDERING STUFF</div>
</div>
</div>
</div>
</div>
)
}
After:
function ExpensiveComponent() {
return (
<div>
<div>
<div>
<div>SUPER EXPENSIVE RENDERING STUFF</div>
</div>
</div>
</div>
)
}
function CounterDisplay() {
let counter = Counter.useContainer()
return (
<div>
<button onClick={counter.decrement}>-</button>
<p>You clicked {counter.count} times</p>
<button onClick={counter.increment}>+</button>
<ExpensiveComponent />
</div>
)
}
Before:
function CounterDisplay(props) {
let counter = Counter.useContainer()
// Recalculating this every time `counter` changes is expensive
let expensiveValue = expensiveComputation(props.input)
return (
<div>
<button onClick={counter.decrement}>-</button>
<p>You clicked {counter.count} times</p>
<button onClick={counter.increment}>+</button>
</div>
)
}
After:
function CounterDisplay(props) {
let counter = Counter.useContainer()
// Only recalculate this value when its inputs have changed
let expensiveValue = useMemo(() => {
return expensiveComputation(props.input)
}, [props.input])
return (
<div>
<button onClick={counter.decrement}>-</button>
<p>You clicked {counter.count} times</p>
<button onClick={counter.increment}>+</button>
</div>
)
}
Before:
function useCounter() {
let [count, setCount] = useState(0)
let decrement = () => setCount(count - 1)
let increment = () => setCount(count + 1)
return { count, decrement, increment }
}
let Counter = createContainer(useCounter)
function CounterDisplay(props) {
let counter = Counter.useContainer()
return (
<div>
<button onClick={counter.decrement}>-</button>
<p>You clicked {counter.count} times</p>
<button onClick={counter.increment}>+</button>
</div>
)
}
After:
function useCounter() {
let [count, setCount] = useState(0)
let decrement = useCallback(() => setCount(count - 1), [count])
let increment = useCallback(() => setCount(count + 1), [count])
return { count, decrement, increment }
}
let Counter = createContainer(useCounter)
let CounterDisplayInner = React.memo(props => {
return (
<div>
<button onClick={props.decrement}>-</button>
<p>You clicked {props.count} times</p>
<button onClick={props.increment}>+</button>
</div>
)
})
function CounterDisplay(props) {
let counter = Counter.useContainer()
return <CounterDisplayInner {...counter} />
}
useMemo()
Before:
function CounterDisplay(props) {
let counter = Counter.useContainer()
let count = counter.count
return (
<p>You clicked {count} times</p>
)
}
After:
function CounterDisplay(props) {
let counter = Counter.useContainer()
let count = counter.count
return useMemo(() => (
<p>You clicked {count} times</p>
), [count])
}
I consider this library the spiritual successor to Unstated. I created Unstated because I believed that React was really great at state management already and the only missing piece was sharing state and logic easily. So I created Unstated to be the "minimal" solution to sharing React state and logic.
However, with Hooks, React has become much better at sharing state and logic. To the point that I think Unstated has become an unnecessary abstraction.
HOWEVER, I think many developers have struggled seeing how to share state and logic with React Hooks for "application state". That may just be an issue of documentation and community momentum, but I think that an API could help bridge that mental gap.
That API is what Unstated Next is. Instead of being the "Minimal API for sharing state and logic in React", it is now the "Minimal API for understanding shared state and logic in React".
I've always been on the side of React. I want React to win. I would like to see the community abandon state management libraries like Redux, and find better ways of making use of React's built-in toolchain.
If instead of using Unstated, you just want to use React itself, I would highly encourage that. Write blog posts about it! Give talks about it! Spread your knowledge in the community.
unstated
I've intentionally published this as a separate package name because it is a complete reset on the API. This way you can have both installed and migrate incrementally.
Please provide me with feedback on that migration process, because over the next few months I hope to take that feedback and do two things:
unstated-next
fulfills all the needs of unstated
users.unstated
has a clean migration process towards unstated-next
.I may choose to add APIs to either library to make life easier for developers. For unstated-next
I promise that the added APIs will be as minimal as possible and I'll try to keep the library small.
In the future, I will likely merge unstated-next
back into unstated
as a new major version. unstated-next
will still exist so that you can have both unstated@2
and unstated-next
installed. Then when you are done with the migration, you can update to unstated@3
and remove unstated-next
(being sure to update all your imports as you do... should be just a find-and-replace).
Even though this is a major new API change, I hope that I can make this migration as easy as possible on you. I'm optimizing for you to get to using the latest React Hooks APIs and not for preserving code written with Unstated.Container
's. Feel free to provide feedback on how that could be done better.
English | 中文 | Русский | ภาษาไทย | Tiếng Việt
(Please contribute translations!)
Author: jamiebuilds
Source Code: https://github.com/jamiebuilds/unstated-next
License: MIT license
1678520405
Telescope
Just another state manager for flutter based on observable design pattern.
Telescope tries to be:
How to use
In 3 easy steps.
var textValue = Telescope("default value");
Put this in middle of you widget build function.
You can watch single telescope in multiple widget
@override
Widget build(BuildContext context) {
return Material(
child: SafeArea(
child: Container(
child: Column(children: [
// watch like this ('this' is State that will automatically rebuild on data change )
Text(textValue.watch(this)),
Text(textValue.watch(this)),
Text(textValue.watch(this).length.toString()),
],),
)
)
);
}
You can update telescope.value from anywhere from your code:
onTap: (){
textValue.value += "a";
}
And state will get update automatically without calling setState
Other features:
Telescopes can be depended on other telescopes
var height = Telescope(186);
var weight = Telescope(72);
var bmi = Telescope.dependsOn([height,weight], () {
return weight.value / ((height.value/100) * (height.value/100));
});
var showingText = Telescope.dependsOn([bmi], () {
return "weight is ${weight.value} and height is ${height.value} so bmi will be ${bmi.value.toString().substring(0,5)}";
});
So when ever height or weight value get changes, the bmi will calculate itself because it depends on height and weight.
And showingText will calculate itself too, because it depends on bmi.
You can save telescope data on disk easily like this:
static var height = Telescope.saveOnDiskForBuiltInType(187, "bmi_height_input");
So if user close the app and open it again it will load last value of telescope for You.
Telescope implementation for list
var items = TelescopeList(["ab", "abb", "bc", "bcc" , "c"]);
You need to implement OnDiskSaveAbility for your object:
For example you have Human class:
class Human{
int height;
int weight;
Human(this.height,this.weight);
@override
int get hashCode => height*weight;
}
Then you need to make other class like this for Human:
class HumanOnDiskAbility implements OnDiskSaveAbility<Human>{
@override
Human parseOnDiskString(String data) {
var sp = data.split(":");
return Human(int.parse(sp[0]), int.parse(sp[1]));
}
@override
String toOnDiskString(Human instance) => "${instance.height}:${instance.weight}";
}
And pass instance of HumanOnDiskAbility to Telescope:
var human = Telescope.saveOnDiskForNonBuiltInType(
Human(187, 72),
"human_for_bmi",
HumanOnDiskAbility()
);
Telescope will use 'parseOnDiskString' and 'toOnDiskString' to serialize and deserialize your object.
This method also can use in TelescopeList in same way.
Examples
Checkout telescope/examples
License GPL3
Run this command:
With Flutter:
$ flutter pub add telescope
This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get
):
dependencies:
telescope: ^1.0.0
Alternatively, your editor might support flutter pub get
. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:telescope/telescope.dart';
void main(){
// every example has a main function. run them
}
Download Details:
Author: ali77gh
Source Code: https://github.com/ali77gh/Telescope
1677131340
Simple global state for React with Hooks API without Context API
This is a library to provide a global state with React Hooks. It has following characteristics.
npm install react-hooks-global-state
import React from 'react';
import { createGlobalState } from 'react-hooks-global-state';
const initialState = { count: 0 };
const { useGlobalState } = createGlobalState(initialState);
const Counter = () => {
const [count, setCount] = useGlobalState('count');
return (
<div>
<span>Counter: {count}</span>
{/* update state by passing callback function */}
<button onClick={() => setCount(v => v + 1)}>+1</button>
{/* update state by passing new value */}
<button onClick={() => setCount(count - 1)}>-1</button>
</div>
);
};
const App = () => (
<>
<Counter />
<Counter />
</>
);
import React from 'react';
import { createStore } from 'react-hooks-global-state';
const reducer = (state, action) => {
switch (action.type) {
case 'increment': return { ...state, count: state.count + 1 };
case 'decrement': return { ...state, count: state.count - 1 };
default: return state;
}
};
const initialState = { count: 0 };
const { dispatch, useStoreState } = createStore(reducer, initialState);
const Counter = () => {
const value = useStoreState('count');
return (
<div>
<span>Counter: {value}</span>
<button onClick={() => dispatch({ type: 'increment' })}>+1</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-1</button>
</div>
);
};
const App = () => (
<>
<Counter />
<Counter />
</>
);
Create a global state.
It returns a set of functions
useGlobalState
: a custom hook works like React.useStategetGlobalState
: a function to get a global state by key outside ReactsetGlobalState
: a function to set a global state by key outside Reactsubscribe
: a function that subscribes to state changesinitialState
Stateimport { createGlobalState } from 'react-hooks-global-state';
const { useGlobalState } = createGlobalState({ count: 0 });
const Component = () => {
const [count, setCount] = useGlobalState('count');
...
};
Create a global store.
It returns a set of functions
useStoreState
: a custom hook to read store state by keygetState
: a function to get store state by key outside Reactdispatch
: a function to dispatch an action to storeA store works somewhat similarly to Redux, but not the same.
reducer
Reducer<State, Action>initialState
State (optional, default (reducer as any)(undefined,{type:undefined})
)enhancer
any?import { createStore } from 'react-hooks-global-state';
const initialState = { count: 0 };
const reducer = ...;
const store = createStore(reducer, initialState);
const { useStoreState, dispatch } = store;
const Component = () => {
const count = useStoreState('count');
...
};
Returns Store<State, Action>
useGlobalState created by createStore is deprecated.
Type: function (stateKey: StateKey): any
Meta
The examples folder contains working examples. You can run one of them with
PORT=8080 npm run examples:01_minimal
and open http://localhost:8080 in your web browser.
You can also try them in codesandbox.io: 01 02 03 04 05 06 07 08 09 10 11 13
Author: Dai-shi
Source Code: https://github.com/dai-shi/react-hooks-global-state
License: MIT license
1676709612
Highlights for State Management in React
🟠 React State Management enables entrepreneurs to build an enterprise app that is scalable, maintainable, and performant.
🟠 There are different ways to effectively apply state management in React js applications: using component state, context API, react & custom hooks, render props, high-order components, and React State Management Libraries.
🟠 The state management libraries in React are pre-built code bundles that are added to the React frontend so that state management of components becomes easy.
🟠 There are various states of React components, such as local, global, fetch, UI state, server-caching, mutable, complex, etc.), and each has its essence.
Read the blog ahead for in-depth information about React State Management.
The most critical and challenging choice of a business owner is to build their enterprise application in such a manner that it is easy to maintain, can be reusable, delivers high performance, and the most essential is that the app must have a good scope of scalability.
React State Management is a striking topic in the web development domain, and when you have a React.js enterprise application, getting an in-depth grasp of the same is vital. As we know the pain points of business app development, let us check how state management React libraries can enable your enterprise app to match your business aims.
State Management in React enables the following:
React.js applications may have difficulty loading the frontend due to the re-renders. With React state management, you can optimize your state updates, resulting in better app performance and efficiency.
State management in React applications enables you to modularize and encapsulate state updates. Hence, you can easily maintain and debug your codebase. This maintainability also ensures that the new development team additions can quickly adapt and understand the applications’ states.
It is difficult to reuse states across various components of a React application, but, using React state management libraries like Redux and MobX, you can easily share states across all the components of your application.
A poor state management strategy leads to performance degradation and bugs, making it difficult to manage the states as the applications scale in size and complexity. React provides a well-designed state management strategy to ensure you can flawlessly scale your React js applications.
This is how State Management React is the wise solution for CTOs and Product owners.
As you have a React Js application offering speed, flexibility, rich UI, and so much more, you would want to leverage the state of components in your application.
Find out the different ways to attain React state management:
Each React component has its internal state, which can be 2used to store and manage data that is specific to that component. This state is managed using the setState method, which updates the component’s state and triggers a re-render.
The Context API is a built-in way to share the state between components in React without passing data down the component tree through props. This can be a useful alternative to using component state when you need to share state between components that are not directly connected in the component tree.
React Hooks are a way to add state and other React features to functional components. The useState and useReducer hooks can be used to manage local component state, while the useContext hook can be used to access shared state from the Context API.
Custom hooks are a way to extract state and logic into reusable functions that multiple components can use. This can be a good option for sharing state and logic between components that are not deeply nested in the component tree.
Higher-Order Components are a way to share the state between components by wrapping components with another component that provides the state. This can be a good option for sharing the state between components not profoundly nested in the component tree.
Render props is a pattern for sharing state between components by passing a function as a prop that renders the component that needs the state. This can be a good option for sharing the state between components not deeply nested in the component tree.
Besides the above, many State Management React Libraries are available for use. Let us find out more about them.
Let us get to a practical where we will learn how to manage states in an React application.
Objective: A simple increment/decrement application in React using the in-built state management features.
npx create-react-app counter-app
Now, as your project is created, navigate to the src directory and create a new file called ‘counter.js’.
First, let’s import React and create a new functional component called Counter. Inside the component, we’ll use the useState hook to create a new state variable called count and set it to an initial value of 0. We’ll also create two functions called increment and decrement that will be used to update the count variable:
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
};
const decrement = () => {
setCount(count - 1);
};
return (
<div>
<h1>{count}</h1>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
</div>
);
}
export default Counter;
In the code above, we’re using the useState hook to create a new state variable called count and a function called setCount that will be used to update the count variable. We’re setting the initial value of count to 0.
We’re also creating two functions called increment and decrement that will be called when the user clicks on the “+” and “-” buttons, respectively. Inside these functions, we’re calling the setCount function to update the value of count by either adding or subtracting 1.
Finally, we’re returning a div element that contains the current value of count, as well as two buttons that call the increment and decrement functions when clicked.
Now, let’s go to App.js file and render the Counter component:
import React from 'react';
import Counter from './Counter';
function App() {
return (
<div>
<Counter />
</div>
);
}
export default App;
You should now be able to run the app by running npm start and see the counter application in your browser.
This was a simple example of handling and managing state in React application.
Here are the popular libraries pre-built for state management in React js applications, along with their pros and cons.
Redux is a popular state management library for building web applications with React, Angular, and other frameworks. It provides a centralized store to manage the state of an application and a set of rules for predictably modifying that state.
Pros: Predictable state management, debuggable, robust ecosystem, time-travel debugging, and efficient handling of complex state changes.
Cons: Can be complex to set up and configure, dependency on boilerplate code, not suitable for simple apps, and requires an understanding of functional programming concepts.
MobX is a react state management library that uses observables to track state changes and automatically re-render components when those observables change.
Pros: Simple and intuitive to use, minimal boilerplate, fast and efficient, excellent performance, and has strong compatibility with React.
Cons: Lacks some features of more complex state management libraries, can be harder to debug, and has a smaller ecosystem.
Recoil is a state management library for React applications that was developed by Facebook. It provides a centralized store to manage the state of an application and a set of hooks for accessing and updating that state.
Pros: Simple, flexible, easy to learn and use, and outstanding performance.
Cons: A relatively new library, which is under development, and the community is not mature, but growing.
Jotai, by Pedro Nauck, uses atoms and setters to manage the state of an application, focusing on simplicity and performance.
Pros: Simple and lightweight, easy to use, works well with React, and has a small learning curve.
Cons: A relatively new library, still under active development, and the ecosystem is not as mature as other libraries.
Guillaume Salva gave the Zustand state management library for React, which uses a simplified Redux-like approach to manage the state of an application.
Pros: Simple and easy to use, with minimal boilerplate code, drives performance and has a small bundle size.
Cons: Not suggested for complex state management handling, and the ecosystem is still growing.
Shawn McKay built this Redux-based state management library for React applications. It provides a simplified API for creating Redux stores and reducers, such that it reduces the dependency on boilerplate and improves developer productivity.
Pros: Easy to use and understand, using lesser boilerplate code than traditional Redux, and provides excellent performance.
Cons: Still requires a good understanding of Redux concepts, may not be suitable for large-scale projects, and has a smaller ecosystem than other options.
Hookstate is a relatively new state management library for React applications that was developed by Rafael Wieland. At its core, Hookstate uses a simplified approach to manage the state of an application, emphasizing performance and developer productivity.
Pros: Simple and easy to use, minimal boilerplate, and delivers high performance.
Cons: Can use for simple state management scenarios only, and has a smaller ecosystem.
Poimandres came up with the Valtio state management library that uses a minimalistic and reactive approach to manage the state of an application, with a focus on performance and developer productivity.
Pros: Simple and easy to use, minimal boilerplate, and excellent performance.
Cons: Can only be used for simple state management scenarios, and has a smaller ecosystem than other options.
David Khourshid developed the XState library, which uses the concept of finite state machines to manage the state of an application, focusing on predictability, modularity, and testability.
Pros: Excellent for managing complex state transitions, with a strong focus on declarative programming and strong typing.
Cons: May require more time to learn and understand and may not be suitable for simpler state management scenarios.
Unstated is a lightweight state management library that uses the React context API to share state between components. It is a simpler alternative to Redux and MobX that can be used for smaller or simpler applications.
Pros: Simple and easy to use, with minimal boilerplate and good performance.
Cons: Not advisable for large-scale projects, and can have potential performance issues as it is dependent on Context API in React, which is slow in certain situations. Has a limited ecosystem and a long learning curve.
Ryan Florence and Michael Jackson developed this library for routing in React apps. React Router provides a declarative way to handle routing in a React application, focusing on simplicity, flexibility, and performance.
Pros: Powerful and flexible routing options and a large ecosystem.
Cons: Can be complex to set up and configure, and can lead to performance issues in some cases.
Adobe developed the React-stately state management library, which at its core, provides a set of hooks and components that can be used to manage complex UI states, such as those found in form inputs, dropdowns, and menus.
Pros: Designed specifically for creating accessible user interfaces with good performance and a clean, composable API.
Cons: Not suitable for more complex state management scenarios.
Fabien Juif developed this library of reusable hooks for React applications. At its core, React Powerhooks provides a set of hooks that manage common UI states, such as loading, error handling, and form validation.
Pros: Provides a variety of useful hooks for common scenarios. These hooks require minimum setup.
Cons: Not suitable for more complex state management scenarios.
Jed Watson developed this library that provides a hook to manage the state of a React component, with a focus on performance and developer productivity.
Pros: Improves the performance of React components that rely heavily on useState.
Cons: Only suitable for some use cases. It is less intuitive to use than the built-in useState hook.
By far, we learned, understood, and discussed the states of components in ReactJs applications and how to handle and manage State Management in React. Let us now get to the core basics.
Here are the different states of React components:
The local state is specific to a single component and is managed using the setState method. It is typically used to stores component-specific data and is only used within that component.
The global state is shared between multiple components and manages the entire application’s global data. It is typically stored in a centralized store, such as a Redux store, an `d accessed by components through the store’s state.
This state is used to manage the data fetched from a remote server or API. The Fetch state is typically used to store information about the state of a fetch operation, such as whether the data has been loaded, if there was an error, or if the data is being loaded.
Manages the data that affects how the UI is displayed- data related to the user interface. Examples of UI state include whether a form is visible or a modal is open, the current selected tab, or the current scroll position.
Stores the state on the server and is used to cache data for performance optimization. Server-side caching state is typically used to store data that does not change frequently, such as information about products or user profiles, to reduce the number of round trips to the server.
It refers to data that can change over time and is typically stored in the component state using the useState hook or in a class component’s state. The mutable state can be simple, such as a string or number, or more complex, such as an array or object. When the state updates, React will re-render the component and any child components that depend on that state.
Refers to data derived from other data and is typically not directly mutable. Instead of being stored in a state, a complex state is calculated using the component’s props or other state variables. Examples of this include the results of a calculation, the filtered or sorted version of an array, or the current state of an animation. Because the complex state is not directly mutable, it doesn’t trigger re-renders of the component when it changes.
React state management is the most essential decision for entrepreneurs to build scalable, performant, and robust React applications. It ensures that our app remains in sync with the user interface. We saw the various in-built as well as third-party options to handle and manage states in React application. From the wide range of solutions, your choice depends on your project requirements, as well as the size of your development team.
Original article source at: https://www.bacancytechnology.com/
1676426820
Dynamic-dependent dropdowns are very helpful to restrict user selection. You may have already seen this on the registration or profile page of the website where you need to select country, state, and city. Options in the dropdown update every time when a country or state dropdown selection gets changed.
If your tables are properly interlinked in the database and want to list their data on the dropdown then you can make them dynamically dependent on one another and load their data using AJAX.
In this tutorial, I show how you can autopopulate dropdown using jQuery AJAX with MySQL database data in CakePHP 4.
In the example, I am using 3 tables –
countries (Store countries name) –
CREATE TABLE `countries` (
`id` int(10) UNSIGNED NOT NULL,
`name` varchar(80) NOT NULL
);
ALTER TABLE `countries`
ADD PRIMARY KEY (`id`);
ALTER TABLE `countries`
MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT;
states (Store country states names) –
Added foreign key on country_id
field.
CREATE TABLE `states` (
`id` int(10) UNSIGNED NOT NULL,
`country_id` int(10) UNSIGNED NOT NULL,
`name` varchar(80) NOT NULL
);
ALTER TABLE `states`
ADD PRIMARY KEY (`id`),
ADD KEY `states_country_id_foreign` (`country_id`);
ALTER TABLE `states`
MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT;
cities (Store state cities name) –
Added foreign key on state_id
field.
CREATE TABLE `cities` (
`id` int(10) UNSIGNED NOT NULL,
`state_id` int(10) UNSIGNED NOT NULL,
`name` varchar(80) NOT NULL
);
ALTER TABLE `cities`
ADD PRIMARY KEY (`id`),
ADD KEY `cities_state_id_foreign` (`state_id`);
ALTER TABLE `cities`
MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT;
config/app_local.php
file.Datasources
default
.'Datasources' => [
'default' => [
'host' => '127.0.0.1',
/*
* CakePHP will use the default DB port based on the driver selected
* MySQL on MAMP uses port 8889, MAMP users will want to uncomment
* the following line and set the port accordingly
*/
//'port' => 'non_standard_port_number',
'username' => 'root',
'password' => 'root',
'database' => 'cakephp4',
/*
* If not using the default 'public' schema with the PostgreSQL driver
* set it here.
*/
//'schema' => 'myapp',
/*
* You can use a DSN string to set the entire configuration
*/
'url' => env('DATABASE_URL', null),
],
/*
* The test connection is used during the test suite.
*/
'test' => [
'host' => 'localhost',
//'port' => 'non_standard_port_number',
'username' => 'my_app',
'password' => 'secret',
'database' => 'test_myapp',
//'schema' => 'myapp',
'url' => env('DATABASE_TEST_URL', 'sqlite://127.0.0.1/tests.sqlite'),
],
],
Create 3 models –
Countries Model –
bin/cake bake model Countries
This will create 2 files –
<?php
declare(strict_types=1);
namespace App\Model\Entity;
use Cake\ORM\Entity;
class Country extends Entity
{
protected $_accessible = [
'name' => true,
'states' => true,
];
}
<?php
declare(strict_types=1);
namespace App\Model\Table;
use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class CountriesTable extends Table
{
public function initialize(array $config): void
{
parent::initialize($config);
$this->setTable('countries');
$this->setDisplayField('name');
$this->setPrimaryKey('id');
$this->hasMany('States', [
'foreignKey' => 'country_id',
]);
}
public function validationDefault(Validator $validator): Validator
{
$validator
->scalar('name')
->maxLength('name', 255)
->requirePresence('name', 'create')
->notEmptyString('name');
return $validator;
}
}
States Model –
bin/cake bake model States
This will create 2 files –
<?php
declare(strict_types=1);
namespace App\Model\Entity;
use Cake\ORM\Entity;
class State extends Entity
{
protected $_accessible = [
'country_id' => true,
'name' => true,
'country' => true,
'cities' => true,
];
}
<?php
declare(strict_types=1);
namespace App\Model\Table;
use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class StatesTable extends Table
{
public function initialize(array $config): void
{
parent::initialize($config);
$this->setTable('states');
$this->setDisplayField('name');
$this->setPrimaryKey('id');
$this->belongsTo('Countries', [
'foreignKey' => 'country_id',
'joinType' => 'INNER',
]);
$this->hasMany('Cities', [
'foreignKey' => 'state_id',
]);
}
public function validationDefault(Validator $validator): Validator
{
$validator
->notEmptyString('country_id');
$validator
->scalar('name')
->maxLength('name', 255)
->requirePresence('name', 'create')
->notEmptyString('name');
return $validator;
}
public function buildRules(RulesChecker $rules): RulesChecker
{
$rules->add($rules->existsIn('country_id', 'Countries'), ['errorField' => 'country_id']);
return $rules;
}
}
Cities Model –
This will create 2 files –
<?php
declare(strict_types=1);
namespace App\Model\Entity;
use Cake\ORM\Entity;
class City extends Entity
{
protected $_accessible = [
'state_id' => true,
'name' => true,
'state' => true,
];
}
<?php
declare(strict_types=1);
namespace App\Model\Table;
use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class CitiesTable extends Table
{
public function initialize(array $config): void
{
parent::initialize($config);
$this->setTable('cities');
$this->setDisplayField('name');
$this->setPrimaryKey('id');
$this->belongsTo('States', [
'foreignKey' => 'state_id',
'joinType' => 'INNER',
]);
}
public function validationDefault(Validator $validator): Validator
{
$validator
->notEmptyString('state_id');
$validator
->scalar('name')
->maxLength('name', 255)
->requirePresence('name', 'create')
->notEmptyString('name');
return $validator;
}
public function buildRules(RulesChecker $rules): RulesChecker
{
$rules->add($rules->existsIn('state_id', 'States'), ['errorField' => 'state_id']);
return $rules;
}
}
AutopopulateController.php
file in src/Controller/
folder.AutopopulateController
Class that extends AppController
.Create 3 method –
countries
table. Loop on the fetched records and store them in $country_arr
Array. Using $this->set()
pass $country_arr
Array to template.Read POST country_id
and assign it to the variable. Fetch all records from the states
table where country_id = $country_id
.
Loop on the fetch records and store state id and name in $data_arr
Array. Return $data_arr
Array in JSON format.
Read POST state_id
and assign it to the variable. Fetch all records from the cities
table where state_id = $state_id
.
Loop on the fetch records and store city id and name in $data_arr
Array. Return $data_arr
Array in JSON format.
Completed Code
<?php
declare(strict_types=1);
namespace App\Controller;
class AutopopulateController extends AppController
{
public function index(){
## Fetch all countries
$countries = $this->getTableLocator()->get('Countries');
$query = $countries->find('all')->order(['name' => 'ASC']);
$countriesList = $query->toArray();
$country_arr = array();
foreach($countriesList as $country){
$country_arr[$country['id']] = $country['name'];
}
$this->set(compact('country_arr'));
}
// Get country states
public function getCountryStates(){
// POST value
$country_id = $this->request->getData()['country_id'];
## Fetch all states of country_id
$states = $this->getTableLocator()->get('States');
$query = $states->find('all')
->where(['country_id' => $country_id])
->order(['name' => 'ASC']);
$statesList = $query->toArray();
$data_arr = array();
foreach($statesList as $state){
$data_arr[] = array(
'id' => $state['id'],
'name' => $state['name']
);
}
echo json_encode($data_arr);
die;
}
// Get state cities
public function getStateCities(){
// POST value
$state_id = $this->request->getData()['state_id'];
## Fetch all cities of state_id
$cities = $this->getTableLocator()->get('Cities');
$query = $cities->find('all')
->where(['state_id' => $state_id])
->order(['name' => 'ASC']);
$citiesList = $query->toArray();
$data_arr = array();
foreach($citiesList as $city){
$data_arr[] = array(
'id' => $city['id'],
'name' => $city['name']
);
}
echo json_encode($data_arr);
die;
}
}
I am including jQuery and CSRF token on templates/layout/default.php
file.
Stored CSRF token in <meta >
tag –
<?= $this->Html->meta('csrfToken', $this->request->getAttribute('csrfToken')); ?>
and jQuery in <head >
section –
<?= $this->Html->script('https://code.jquery.com/jquery.min.js'); ?>
Completed Code
<?php
$cakeDescription = 'CakePHP: the rapid development php framework';
?>
<!DOCTYPE html>
<html>
<head>
<?= $this->Html->charset() ?>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- CSRF Token -->
<?= $this->Html->meta('csrfToken', $this->request->getAttribute('csrfToken')); ?>
<title>
<?= $cakeDescription ?>:
<?= $this->fetch('title') ?>
</title>
<?= $this->Html->meta('icon') ?>
<link href="https://fonts.googleapis.com/css?family=Raleway:400,700" rel="stylesheet">
<?= $this->Html->css(['normalize.min', 'milligram.min', 'cake']) ?>
<!-- jQuery -->
<?= $this->Html->script('https://code.jquery.com/jquery.min.js'); ?>
<?= $this->fetch('meta') ?>
<?= $this->fetch('css') ?>
<?= $this->fetch('script') ?>
</head>
<body>
<nav class="top-nav">
<div class="top-nav-title">
<a href="<?= $this->Url->build('/') ?>"><span>Cake</span>PHP</a>
</div>
<div class="top-nav-links">
<a target="_blank" rel="noopener" href="https://book.cakephp.org/4/">Documentation</a>
<a target="_blank" rel="noopener" href="https://api.cakephp.org/">API</a>
</div>
</nav>
<main class="main">
<div class="container">
<?= $this->Flash->render() ?>
<?= $this->fetch('content') ?>
</div>
</main>
<footer>
</footer>
</body>
</html>
Create Autopopulate
folder in templates/
location. In the Autopopulate
folder create index.php
file – templates/Autopopulate/index.php
.
Create 3 <select >
elements –
jQuery
Read CSRF token from the <meta >
tag and assign it to csrfToken
variable.
Country Change –
Define change
event on #country_id
. Read selected country_id
and empty the state and city dropdown. If country_id
is not empty then send AJAX POST request to <?= $this->Url->build(['controller' => 'Autopopulate','action' => 'getCountryStates']) ?>
.
Here, pass country_id – {country_id: country_id}
as data, set dataType: 'json'
, pass CSRF token using header.
On successful callback loop on the response
and add a new <option >
in state dropdown.
State Change –
Define change
event on #state_id
. Read selected state_id
and empty the city dropdown. If state_id
is not empty then send AJAX POST request to <?= $this->Url->build(['controller' => 'Autopopulate','action' => 'getStateCities']) ?>
.
Here, pass state_id – {state_id: state_id}
as data, set dataType: 'json'
, pass CSRF token using header.
On successful callback loop on the response
to add <option >
in city dropdown.
Completed Code
<div class="row">
<div class="col-6">
<!-- Country -->
<div id="input">
<label for='country_id'>Country</label>
<?php
echo $this->Form->select(
'country_id',
$country_arr,
[
'id' => 'country_id',
'empty' => '-- Select Country --'
]
);
?>
</div>
<!-- State -->
<div id="input">
<label for='state_id'>State</label>
<?php
echo $this->Form->select(
'state_id',
[],
[
'id' => 'state_id',
'empty' => '-- Select State --'
]
);
?>
</div>
<!-- City -->
<div id="input">
<label for='city_id'>City</label>
<?php
echo $this->Form->select(
'city_id',
[],
[
'id' => 'city_id',
'empty' => '-- Select City --'
]
);
?>
</div>
</div>
</div>
<!-- Script -->
<script type="text/javascript">
// Read CSRF Token
var csrfToken = $('meta[name="csrfToken"]').attr('content');
$(document).ready(function(){
// Country change
$('#country_id').change(function(){
var country_id = $('#country_id').val();
// Empty state and city dropdown
$('#state_id').find('option').not(':first').remove();
$('#city_id').find('option').not(':first').remove();
if(country_id != ''){
// AJAX request
$.ajax({
url: "<?= $this->Url->build(['controller' => 'Autopopulate','action' => 'getCountryStates']) ?>",
type: 'post',
data: {country_id: country_id},
dataType: 'json',
headers:{
'X-CSRF-Token': csrfToken
},
success: function(response){
var len = response.length;
// Add response data to state dropdown
for( var i = 0; i<len; i++){
var id = response[i]['id'];
var name = response[i]['name'];
$("#state_id").append("<option value='"+id+"'>"+name+"</option>");
}
},
});
}
});
// State change
$('#state_id').change(function(){
var state_id = $('#state_id').val();
// Empty city dropdown
$('#city_id').find('option').not(':first').remove();
if(state_id != ''){
// AJAX request
$.ajax({
url: "<?= $this->Url->build(['controller' => 'Autopopulate','action' => 'getStateCities']) ?>",
type: 'post',
data: {state_id: state_id},
dataType: 'json',
headers:{
'X-CSRF-Token': csrfToken
},
success: function(response){
var len = response.length;
// Add response data to city dropdown
for( var i = 0; i<len; i++){
var id = response[i]['id'];
var name = response[i]['name'];
$("#city_id").append("<option value='"+id+"'>"+name+"</option>");
}
},
});
}
});
});
</script>
In the example, I returned a JSON response from the server and looped on it to add a new item to the dropdown, but you can also return an HTML response and directly append it to the dropdown.
After implementing this on your project if data is not loading in the dropdown then debug it using the browser console and network tab.
If you found this tutorial helpful then don't forget to share.
Original article source at: https://makitweb.com/
1673463960
The <1 kb state machine hook for React:
See the user-facing docs at: usestatemachine.js.org
npm install @cassiozen/usestatemachine
const [state, send] = useStateMachine({
initial: 'inactive',
states: {
inactive: {
on: { TOGGLE: 'active' },
},
active: {
on: { TOGGLE: 'inactive' },
effect() {
console.log('Just entered the Active state');
// Same cleanup pattern as `useEffect`:
// If you return a function, it will run when exiting the state.
return () => console.log('Just Left the Active state');
},
},
},
});
console.log(state); // { value: 'inactive', nextEvents: ['TOGGLE'] }
// Refers to the TOGGLE event name for the state we are currently in.
send('TOGGLE');
// Logs: Just entered the Active state
console.log(state); // { value: 'active', nextEvents: ['TOGGLE'] }
API
useStateMachine
const [state, send] = useStateMachine(/* State Machine Definition */);
useStateMachine
takes a JavaScript object as the state machine definition. It returns an array consisting of a current machine state
object and a send
function to trigger transitions.
The machine's state
consists of 4 properties: value
, event
, nextEvents
and context
.
value
(string): Returns the name of the current state.
event
({type: string}
; Optional): The name of the last sent event that led to this state.
nextEvents
(string[]
): An array with the names of available events to trigger transitions from this state.
context
: The state machine extended state. See "Extended State" below.
send
takes an event as argument, provided in shorthand string format (e.g. "TOGGLE") or as an event object (e.g. { type: "TOGGLE" }
)
If the current state accepts this event, and it is allowed (see guard), it will change the state machine state and execute effects.
You can also send additional data with your event using the object notation (e.g. { type: "UPDATE" value: 10 }
). Check schema for more information about strong typing the additional data.
State Machine definition
Key | Required | Description |
---|---|---|
verbose | If true, will log every context & state changes. Log messages will be stripped out in the production build. | |
schema | For usage with TypeScript only. Optional strongly-typed context & events. More on schema below | |
context | Context is the machine's extended state. More on extended state below | |
initial | * | The initial state node this machine should be in |
states | * | Define the possible finite states the state machine can be in. |
A finite state machine can be in only one of a finite number of states at any given time. As an application is interacted with, events cause it to change state.
States are defined with the state name as a key and an object with two possible keys: on
(which events this state responds to) and effect
(run arbitrary code when entering or exiting this state):
Describes which events this state responds to (and to which other state the machine should transition to when this event is sent):
states: {
inactive: {
on: {
TOGGLE: 'active';
}
},
active: {
on: {
TOGGLE: 'inactive';
}
},
},
The event definition can also use the extended, object syntax, which allows for more control over the transition (like adding guards):
on: {
TOGGLE: {
target: 'active',
},
};
Guards are functions that run before actually making the state transition: If the guard returns false the transition will be denied.
const [state, send] = useStateMachine({
initial: 'inactive',
states: {
inactive: {
on: {
TOGGLE: {
target: 'active',
guard({ context, event }) {
// Return a boolean to allow or block the transition
},
},
},
},
active: {
on: { TOGGLE: 'inactive' },
},
},
});
The guard function receives an object with the current context and the event. The event parameter always uses the object format (e.g. { type: 'TOGGLE' }
).
Effects are triggered when the state machine enters a given state. If you return a function from your effect, it will be invoked when leaving that state (similarly to how useEffect works in React).
const [state, send] = useStateMachine({
initial: 'active',
states: {
active: {
on: { TOGGLE: 'inactive' },
effect({ send, setContext, event, context }) {
console.log('Just entered the Active state');
return () => console.log('Just Left the Active state');
},
},
},
});
The effect function receives an object as parameter with four keys:
send
: Takes an event as argument, provided in shorthand string format (e.g. "TOGGLE") or as an event object (e.g. { type: "TOGGLE" }
)setContext
: Takes an updater function as parameter to set a new context (more on context below). Returns an object with send
, so you can set the context and send an event on a single line.event
: The event that triggered a transition to this state. (The event parameter always uses the object format (e.g. { type: 'TOGGLE' }
).).context
The context at the time the effect runs.In this example, the state machine will always send the "RETRY" event when entering the error state:
const [state, send] = useStateMachine({
initial: 'loading',
states: {
/* Other states here... */
error: {
on: {
RETRY: 'load',
},
effect({ send }) {
send('RETRY');
},
},
},
});
Besides the finite number of states, the state machine can have extended state (known as context).
You can provide the initial context value in the state machine definition, then use the setContext
function within your effects to change the context:
const [state, send] = useStateMachine({
context: { toggleCount: 0 },
initial: 'inactive',
states: {
inactive: {
on: { TOGGLE: 'active' },
},
active: {
on: { TOGGLE: 'inactive' },
effect({ setContext }) {
setContext(context => ({ toggleCount: context.toggleCount + 1 }));
},
},
},
});
console.log(state); // { context: { toggleCount: 0 }, value: 'inactive', nextEvents: ['TOGGLE'] }
send('TOGGLE');
console.log(state); // { context: { toggleCount: 1 }, value: 'active', nextEvents: ['TOGGLE'] }
TypeScript will automatically infer your context type; event types are generated automatically.
Still, there are situations where you might want explicit control over the context
and event
types: You can provide you own typing using the t
whithin schema
:
Typed Context example
const [state, send] = useStateMachine({
schema: {
context: t<{ toggleCount: number }>()
},
context: { toggleCount: 0 },
initial: 'inactive',
states: {
inactive: {
on: { TOGGLE: 'active' },
},
active: {
on: { TOGGLE: 'inactive' },
effect({ setContext }) {
setContext(context => ({ toggleCount: context.toggleCount + 1 }));
},
},
},
});
Typed Events
All events are type-infered by default, both in the string notation (send("UPDATE")
) and the object notation (send({ type: "UPDATE"})
).
If you want, though, you can augment an already typed event to include arbitrary data (which can be useful to provide values to be used inside effects or to update the context). Example:
const [machine, send] = useStateMachine({
schema: {
context: t<{ timeout?: number }>(),
events: {
PING: t<{ value: number }>()
}
},
context: {timeout: undefined},
initial: 'waiting',
states: {
waiting: {
on: {
PING: 'pinged'
}
},
pinged: {
effect({ setContext, event }) {
setContext(c => ({ timeout: event?.value ?? 0 }));
},
}
},
});
send({ type: 'PING', value: 150 })
Note that you don't need to declare all your events in the schema, only the ones you're adding arbitrary keys and values.
Wiki
Author: Cassiozen
Source Code: https://github.com/cassiozen/useStateMachine
License: MIT license
1673460600
It is an Ansible role to:
docker
PIP package so Ansible's docker_*
modules workIf you're like me, you probably love Docker. This role provides everything you need to get going with a production ready Docker host.
By the way, if you don't know what Docker is, or are looking to become an expert with it then check out Dive into Docker: The Complete Docker Course for Developers.
You are viewing the master branch's documentation which might be ahead of the latest release. Switch to the latest release.
The philosophy for all of my roles is to make it easy to get going, but provide a way to customize nearly everything.
The latest Docker CE, Docker Compose v1 and Docker Compose v2 will be installed, Docker disk clean up will happen once a week and Docker container logs will be sent to journald
.
---
# docker.yml
- name: Example
hosts: "all"
become: true
roles:
- role: "nickjj.docker"
tags: ["docker"]
Usage: ansible-playbook docker.yml
$ ansible-galaxy install nickjj.docker
Do you want to use "ce" (community edition) or "ee" (enterprise edition)?
docker__edition: "ce"
Do you want to use the "stable", "edge", "testing" or "nightly" channels? You can add more than one (order matters).
docker__channel: ["stable"]
docker__version: ""
# For example, pin it to 20.10.
docker__version: "20.10"
# For example, pin it to a more precise version of 20.10.
docker__version: "20.10.17"
Pins are set with *
at the end of the package version so you will end up getting minor and security patches unless you pin an exact version.
Upgrade strategy
"present"
, running this role in the future won't install newer versions (if available)"latest"
, running this role in the future will install newer versions (if available)docker__state: "present"
Downgrade strategy
The easiest way to downgrade would be to uninstall the Docker package manually and then run this role afterwards while pinning whatever specific Docker version you want.
# An ad-hoc Ansible command to stop and remove the Docker CE package on all hosts.
ansible all -m systemd -a "name=docker-ce state=stopped" \
-m apt -a "name=docker-ce autoremove=true purge=true state=absent" -b
Docker Compose v2 will get apt installed using the official docker-compose-plugin
that Docker manages.
docker__compose_v2_version: ""
# For example, pin it to 2.6.
docker__compose_v2_version: "2.6"
# For example, pin it to a more precise version of 2.6.
docker__compose_v2_version: "2.6.0"
Upgrade strategy
It'll re-use the docker__state
variable explained above in the Docker section with the same rules.
Downgrade strategy
Like Docker itself, the easiest way to uninstall Docker Compose v2 is to manually run the command below and then pin a specific Docker Compose v2 version.
# An ad-hoc Ansible command to remove the Docker Compose Plugin package on all hosts.
ansible all -m apt -a "name=docker-compose-plugin autoremove=true purge=true state=absent" -b
Docker Compose v1 will get PIP installed inside of a Virtualenv. If you plan to use Docker Compose v2 instead it will be very easy to skip installing v1 although technically both can be installed together since v1 is accessed with docker-compose
and v2 is accessed with docker compose
(notice the lack of hyphen).
In any case details about this is covered in detail in a later section of this README file.
docker__compose_version: ""
# For example, pin it to 1.29.
docker__compose_version: "1.29"
# For example, pin it to a more precise version of 1.29.
docker__compose_version: "1.29.2"
Upgrade and downgrade strategies will be explained in the other section of this README.
A list of users to be added to the docker
group.
Keep in mind this user needs to already exist, this role will not create it. If you want to create users, check out my user role.
This role does not configure User Namespaces or any other security features by default. If the user you add here has SSH access to your server then you're effectively giving them root access to the server since they can run Docker without sudo
and volume mount in any path on your file system.
In a controlled environment this is safe, but like anything security related it's worth knowing this up front. You can enable User Namespaces and any other options with the docker__daemon_json
variable which is explained later.
# Try to use the sudo user by default, but fall back to root.
docker__users: ["{{ ansible_env.SUDO_USER | d('root') }}"]
# For example, if the user you want to set is different than the sudo user.
docker__users: ["admin"]
Login to 1 or more Docker registries (such as the Docker Hub).
# Your login credentials will end up in this user's home directory.
docker__login_become_user: "{{ docker__users | first | d('root') }}"
# 0 or more registries to log into.
docker__registries:
- #registry_url: "https://index.docker.io/v1/"
username: "your_docker_hub_username"
password: "your_docker_hub_password"
#email: "your_docker_hub@emailaddress.com"
#reauthorize: false
#config_path: "$HOME/.docker/config.json"
#state: "present"
docker__registries: []
Properties prefixed with * are required.
registry_url
defaults to https://index.docker.io/v1/
username
is your Docker registry usernamepassword
is your Docker registry passwordemail
defaults to not being used (not all registries use it)reauthorize
defaults to false
, when true
it updates your credentialsconfig_path
defaults to your docker__login_become_user
's $HOME
directorystate
defaults to "present", when "absent" the login will be removedDefault Docker daemon options as they would appear in /etc/docker/daemon.json
.
docker__default_daemon_json: |
"log-driver": "journald",
"features": {
"buildkit": true
}
# Add your own additional daemon options without overriding the default options.
# It follows the same format as the default options, and don't worry about
# starting it off with a comma. The template will add the comma if needed.
docker__daemon_json: ""
Flags that are set when starting the Docker daemon cannot be changed in the daemon.json
file. By default Docker sets -H unix://
which means that option cannot be changed with the json options.
Add or change the starting Docker daemon flags by supplying them exactly how they would appear on the command line.
# Each command line flag should be its own item in the list.
#
# Using a Docker version prior to 18.09?
# You must set `-H fd://` instead of `-H unix://`.
docker__daemon_flags:
- "-H unix://"
If you don't supply some type of -H
flag here, Docker will fail to start.
docker__daemon_environment: []
# For example, here's how to set a couple of proxy environment variables.
docker__daemon_environment:
- "HTTP_PROXY=http://proxy.example.com:80"
- "HTTPS_PROXY=https://proxy.example.com:443"
This role lets the Docker package manage its own systemd unit file and adjusts things like the Docker daemon flags and environment variables by using the systemd override pattern.
If you know what you're doing, you can override or add to any of Docker's systemd directives by setting this variable. Anything you place in this string will be written to /etc/systemd/system/docker.service.d/custom.conf
as is.
docker__systemd_override: ""
By default this will safely clean up disk space used by Docker every Sunday at midnight.
# `a` removes unused images (useful in production).
# `f` forces it to happen without prompting you to agree.
docker__cron_jobs_prune_flags: "af"
# Control the schedule of the docker system prune.
docker__cron_jobs_prune_schedule: ["0", "0", "*", "*", "0"]
docker__cron_jobs:
- name: "Docker disk clean up"
job: "docker system prune -{{ docker__cron_jobs_prune_flags }} > /dev/null 2>&1"
schedule: "{{ docker__cron_jobs_prune_schedule }}"
cron_file: "docker-disk-clean-up"
#user: "{{ (docker__users | first) | d('root') }}"
#state: "present"
Properties prefixed with * are required.
name
is the cron job's descriptionjob
is the command to run in the cron jobschedule
is the standard cron job format for every Sunday at midnightcron_file
writes a cron file to /etc/cron.d
instead of a user's individual crontabuser
defaults to the first docker__users
user or root if that's not availablestate
defaults to "present", when "absent" the cron file will be removedDocker requires a few dependencies to be installed for it to work. You shouldn't have to edit any of these variables.
# List of packages to be installed.
docker__package_dependencies:
- "apt-transport-https"
- "ca-certificates"
- "cron"
- "gnupg2"
- "software-properties-common"
# Ansible identifies CPU architectures differently than Docker.
docker__architecture_map:
"x86_64": "amd64"
"aarch64": "arm64"
"aarch": "arm64"
"armhf": "armhf"
"armv7l": "armhf"
# The Docker GPG key id used to sign the Docker package.
docker__apt_key_id: "9DC858229FC7DD38854AE2D88D81803C0EBFCD88"
# The Docker GPG key server address.
docker__apt_key_url: "https://download.docker.com/linux/{{ ansible_distribution | lower }}/gpg"
# The Docker upstream APT repository.
docker__apt_repository: >
deb [arch={{ docker__architecture_map[ansible_architecture] }}]
https://download.docker.com/linux/{{ ansible_distribution | lower }}
{{ ansible_distribution_release }} {{ docker__channel | join (' ') }}
Rather than pollute your server's version of Python, all PIP packages are installed into a Virtualenv of your choosing.
docker__pip_virtualenv: "/usr/local/lib/docker/virtualenv"
This role installs PIP because Docker Compose v1 is installed with the docker-compose
PIP package and Ansible's docker_*
modules use the docker
PIP package.
docker__pip_dependencies:
- "gcc"
- "python3-setuptools"
- "python3-dev"
- "python3-pip"
- "virtualenv"
docker__default_pip_packages:
- name: "docker"
state: "{{ docker__pip_docker_state }}"
- name: "docker-compose"
version: "{{ docker__compose_version }}"
path: "/usr/local/bin/docker-compose"
src: "{{ docker__pip_virtualenv + '/bin/docker-compose' }}"
state: "{{ docker__pip_docker_compose_state }}"
# Add your own PIP packages with the same properties as above.
docker__pip_packages: []
Properties prefixed with * are required.
name
is the package nameversion
is the package version to be installed (or "" if this is not defined)path
is the destination path of the symlinksrc
is the source path to be symlinkedstate
defaults to "present", other values can be "forcereinstall" or "absent"PIP package state
"present"
, the package will be installed but not updated on future runs"forcereinstall"
, the package will always be (re)installed and updated on future runs"absent"
, the package will be removeddocker__pip_docker_state: "present"
docker__pip_docker_compose_state: "present"
Skipping the installation of Docker Compose v1
You can set docker__pip_docker_compose_state: "absent"
in your inventory. That's it!
Honestly, in the future I think this will be the default behavior. Since Docker Compsose v2 is still fairly new I wanted to ease into using v2. There's also no harm in having both installed together. You can pick which one to use.
docker_*
modulesThis role uses docker_login
to login to a Docker registry, but you may also use the other docker_*
modules in your own roles. They are not going to work unless you instruct Ansible to use this role's Virtualenv.
At either the inventory, playbook or task level you'll need to set ansible_python_interpreter: "/usr/bin/env python3-docker"
. This works because this role creates a proxy script from the Virtualenv's Python binary to python3-docker
.
You can look at this role's docker_login
task as an example on how to do it at the task level.
Author: nickjj
Source Code: https://github.com/nickjj/ansible-docker
License: MIT license
1670592011
By auto-populating dropdown you can restrict users selection based on the parent dropdown selection.
Data is changed on child dropdowns every time selection is changed.
In this tutorial, I show how you can create dynamic dependent dropdown with PostgreSQL data using jQuery AJAX and PHP.
I am using 3 tables in the example –
countries table (Store countries records) –
CREATE TABLE countries (
id serial PRIMARY KEY,
name varchar(80) NOT NULL
)
states table (Store states of the countries) –
CREATE TABLE states (
id serial PRIMARY KEY,
name varchar(80) NOT NULL,
country_id bigint NOT NULL
)
cities table (Store cities of the states) –
CREATE TABLE cities (
id serial PRIMARY KEY,
name varchar(80) NOT NULL,
state_id bigint NOT NULL
)
Create a new config.php
file.
Completed Code
<?php
$host = "localhost";
$user = "postgres";
$password = "root";
$dbname = "tutorial";
$con = pg_connect("host=$host dbname=$dbname user=$user password=$password");
if (!$con) {
die('Connection failed.');
}
Fetch records from countries
table and create 3 <select>
elements –
<select >
element is to display fetched countries
.Completed Code
<?php
include "config.php";
$sql = "select * from countries order by name";
$result = pg_query($con, $sql);
?>
<table>
<tr>
<td>Country</td>
<td>
<select id="country">
<option value="0" >– Select Country –</option>
<?php
while ($row = pg_fetch_assoc($result) ){
$id = $row['id'];
$name = $row['name'];
echo "<option value='".$id."' >".$name."</option>";
}
?>
</select>
</td>
</tr>
<tr>
<td>State</td>
<td>
<select id="state" >
<option value="0" >– Select State –</option>
</select>
</td>
</tr>
<tr>
<td>City</td>
<td>
<select id="city" >
<option value="0" >– Select City –</option>
</select>
</td>
</tr>
</table>
Create ajaxfile.php
file.
Handle 2 AJAX requests –
states
table according to $country_id
value and assign to $result
. Loop on $result
and initialize $data
Array with id
and name
keys.Return $data
in JSON format.
cities
table according to $state_id
value and assign to $result
. Loop on $result
and initialize $data
Array with id
and name
keys.Return $data
in JSON format.
Completed Code
<?php
include 'config.php';
$request = "";
if(isset($_POST['request'])){
$request = $_POST['request'];
}
// Get states
if($request == 'getStates'){
$country_id = 0;
$result = array();$data = array();
if(isset($_POST['country_id'])){
$country_id = $_POST['country_id'];
$sql = "select * from states where country_id=$1";
$result = pg_query_params($con, $sql, array($country_id));
while ($row = pg_fetch_assoc($result) ){
$id = $row['id'];
$name = $row['name'];
$data[] = array(
"id" => $id,
"name" => $name
);
}
}
echo json_encode($data);
die;
}
// Get cities
if($request == 'getCities'){
$state_id = 0;
$result = array();$data = array();
if(isset($_POST['state_id'])){
$state_id = $_POST['state_id'];
$sql = "select * from cities where state_id=$1";
$result = pg_query_params($con, $sql, array($state_id));
while ($row = pg_fetch_assoc($result) ){
$id = $row['id'];
$name = $row['name'];
$data[] = array(
"id" => $id,
"name" => $name
);
}
}
echo json_encode($data);
die;
}
Define change
event on #country
and #state
.
#state
, and #city
dropdown. Send AJAX POST request to ajaxfile.php
, pass {request: 'getStates', country_id: country_id}
as data
, and set dataType: 'json'
.On successful callback loop on response
and add <option >
in #state
.
#city
dropdown and send AJAX POST request to ajaxfile.php
, pass {request: 'getCities', state_id: state_id}
as data
, and set dataType: 'json'
.On successful callback loop on response
and add <option >
in #city
.
Completed Code
$(document).ready(function(){
// Country
$('#country').change(function(){
// Country id
var country_id = $(this).val();
// Empty the dropdown
$('#state').find('option').not(':first').remove();
$('#city').find('option').not(':first').remove();
// AJAX request
$.ajax({
url: 'ajaxfile.php',
type: 'post',
data: {request: 'getStates', country_id: country_id},
dataType: 'json',
success: function(response){
var len = 0;
if(response != null){
len = response.length;
}
if(len > 0){
// Read data and create <option >
for(var i=0; i<len; i++){
var id = response[i].id;
var name = response[i].name;
var option = "<option value='"+id+"'>"+name+"</option>";
$("#state").append(option);
}
}
}
});
});
// Country
$('#state').change(function(){
// State id
var state_id = $(this).val();
// Empty the dropdown
$('#city').find('option').not(':first').remove();
// AJAX request
$.ajax({
url: 'ajaxfile.php',
type: 'post',
data: {request: 'getCities', state_id: state_id},
dataType: 'json',
success: function(response){
var len = 0;
if(response != null){
len = response.length;
}
if(len > 0){
// Read data and create <option >
for(var i=0; i<len; i++){
var id = response[i].id;
var name = response[i].name;
var option = "<option value='"+id+"'>"+name+"</option>";
$("#city").append(option);
}
}
}
});
});
});
In the example, I am auto-populating two dropdowns but you can follow the same steps to add it on more dropdowns.
If data is not loading in the dropdown then use the browser network tab to debug.
If you found this tutorial helpful then don't forget to share.
Original article source at: https://makitweb.com/
1669018338
Let's learn the difference between Props and State in React.
In React library, props and states are both means to make a component more dynamic. Props (or properties) are inputs passed down from a parent component to its child component. On the other hand, states are variables defined and managed by the component.
In React library, props and states are both means to make a component more dynamic. Props (or properties) are inputs passed down from a parent component to its child component. On the other hand, states are variables defined and managed by the component.
For example, let’s say we have a that calls a :
function ParentComponent(){
return <ChildComponent />
}
You can pass a prop from ParentComponent into ChildComponent by adding new attributes after the component name. For example, the name
prop with value John
is passed to ChildComponent below:
function ParentComponent(){
return <ChildComponent name="John" />
}
After that, the child component will accept the props as the first argument. What to do with the prop being passed down to the child is of no concern to the parent component. You can simply output the name
prop like this:
function ChildComponent(props){
return <p> Hello World! my name is {props.name}</p>
}
In most real projects, the child component would probably render conditionally based on the props being passed into it:
export default function ParentComponent() {
return <ChildComponent name="John" />
}
function ChildComponent(props) {
if (props.name) {
return <p> Hello World! my name is {props.name}</p>
}
return <p>Name not found. Please send me a "name" . . . </p>
}
You can pass as many props as you want into a single child component, just write the props next to the previous. Here’s an example:
function ParentComponent() {
return (
<ChildComponent
name="John"
Age={29}
isMale={true}
hobbies={["read books", "drink coffee"]}
occupation="Software Engineer"
/>
);
}
Oh and please remember: you need to pass either a string or a JavaScript expression inside curly brackets as the value of props. This is because your component call is in JSX syntax, and you need to use curly brackets to write an expression.
Meaning that a prop’s value can’t be changed no matter what happens. The following example will still output “John” instead of “Mark”:
function ChildComponent(props) {
if (props.name) {
props.name = "Mark";
return <p> Hello World! my name is {props.name}</p>;
}
return <p>Name not found. Please send me a "name" . . . </p>;
}
But what if you need variables that might change later? This is where state comes in. States are arbitrary data that you can define, but they are initialized and managed by a component. Here’s how you define a function component state:
function ParentComponent() {
const [name, setName] = React.useState("John")
}
In the example below, the name
state is initialized by calling React.useState
function. The useState
function returns an array with two elements, the first element is the value of the state (in this case, “John”) and the second element is a function that you need to call to change the state value.
The square bracket surrounding both name
and setName
is a destructuring array assignment, meaning we take the return value of useState
function, and directly assign them to name
and setName
variables.
Please note that you can’t change state value by reassigning the variable:
function ParentComponent() {
const [name, setName] = React.useState("John")
name = "Mark"
}
You need to use the function returned by useState
as the second element:
function ParentComponent() {
const [name, setName] = React.useState("");
if (name === "") {
setName("Mark");
}
}
Also, never call on the set function at the top-level since it will cause an infinite loop:
function ParentComponent() {
const [name, setName] = React.useState("");
setName("Mark");
}
You can pass both state and its update function into any child component as a prop when required:
export default function ParentComponent() {
const [name, setName] = React.useState("John");
return <ChildComponent name={name} setName={setName} />;
}
When a child component needs the state to change, you can do so by calling on the setName
function. In the following example, I put a button to change the value of name
when it gets clicked:
function ChildComponent(props) {
if (props.name) {
return (
<>
<p> Hello World! my name is {props.name}</p>
<button onClick={() => props.setName("Mark")}>Change name</button>
</>
);
}
return <p>Name not found. Please send me a "name" . . . </p>;
}
Here’s a codesandbox example that you can play with:
You’ve just learned the difference between props and state in React. Both features are simply arbitrary variables that you can use to make your React components more dynamic. In most cases, states are initialized at top-level components and passed down as props into children components.
When you need to change the prop value, the child component can send a signal — usually function call when a button is clicked or something similar — to the parent component to change the state. This will make the props value being passed down from the parent component to child component changes.
The components will then render the user interface accordingly.
Original article source at: https://sebhastian.com/