Skip to main content سوق المهارات اكتشف واستكشف مهارات الذكاء الاصطناعي التي بناها المجتمع.
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
نسخ Promptعرض تفاصيل Prompt يتجاوز الأمر المباشر Prompt المخصّص للمراجعة. افحص المصدر قبل تشغيله.
npx skills add https://github.com/BEKO2210/Firstbrain --skill fp-reactيبقى الأمر في سطر واحد. مرّر أفقيًا لمراجعته كاملًا قبل النسخ.
تفضّل نسخة محلية؟ نزّل الملفات المتاحة حاليًا لدى SkillsMP.
تحميل Zip جاري التحميل... المهن ذات الصلة SOC
استنادا إلى تصنيف SOC المهني
name fp-react description Practical patterns for using fp-ts with React - hooks, state, forms, data fetching. Works with React 18/19, Next.js 14/15. type skill created 2026-02-27T00:00:00.000Z domain software-development category frontend risk unknown source community tags ["skill","software-development","frontend","react"]
Functional Programming in React
Practical patterns for React apps. No jargon, just code that works.
Quick Reference
Pattern Use When OptionValue might be missing (user not loaded yet) EitherOperation might fail (form validation) TaskEitherAsync operation might fail (API calls) RemoteDataNeed to show loading/error/success states pipeChaining multiple transformations
1. State with Option (Maybe It's There, Maybe Not)
Use Option instead of null | undefined for clearer intent.
Basic Pattern
import { useState } from 'react'
import * as O from 'fp-ts/Option'
import { pipe } from 'fp-ts/function'
interface User {
id : string
name : string
email : string
}
function UserProfile ( ) {
const [user, setUser] = useState<O.Option <User >>(O.none )
const handleLogin = (userData : User ) => {
(O. (userData))
}
= ( ) => {
(O. )
}
(
user,
O. (
,
(
)
)
)
}
setUser
some
const
handleLogout
setUser
none
return
pipe
match
() =>
<button onClick ={() => handleLogin({ id: '1', name: 'Alice', email: 'alice@example.com' })}>
Log In
</button >
(u ) =>
<div >
<p > Welcome, {u.name}!</p >
<button onClick ={handleLogout} > Log Out</button >
</div >
Chaining Optional Values import * as O from 'fp-ts/Option'
import { pipe } from 'fp-ts/function'
interface Profile {
user : O.Option <{
name : string
settings : O.Option <{
theme : string
}>
}>
}
function getTheme (profile : Profile ): string {
return pipe (
profile.user ,
O.flatMap (u => u.settings ),
O.map (s => s.theme ),
O.getOrElse (() => 'light' )
)
}
2. Form Validation with Either Either is perfect for validation: Left = errors, Right = valid data.
Simple Form Validation import * as E from 'fp-ts/Either'
import * as A from 'fp-ts/Array'
import { pipe } from 'fp-ts/function'
const validateEmail = (email : string ): E.Either <string , string > =>
email.includes ('@' )
? E.right (email)
: E.left ('Invalid email address' )
const validatePassword = (password : string ): E.Either <string , string > =>
password.length >= 8
? E.right (password)
: E.left ('Password must be at least 8 characters' )
const validateName = (name : string ): E.Either <string , string > =>
name.trim ().length > 0
? E.right (name.trim ())
: E.left ('Name is required' )
Collecting All Errors (Not Just First One) import * as E from 'fp-ts/Either'
import { sequenceS } from 'fp-ts/Apply'
import { getSemigroup } from 'fp-ts/NonEmptyArray'
import { pipe } from 'fp-ts/function'
const validateAll = sequenceS (E.getApplicativeValidation (getSemigroup<string >()))
interface SignupForm {
name : string
email : string
password : string
}
interface ValidatedForm {
name : string
email : string
password : string
}
function validateForm (form : SignupForm ): E.Either <string [], ValidatedForm > {
return pipe (
validateAll ({
name : pipe (validateName (form.name ), E.mapLeft (e => [e])),
email : pipe (validateEmail (form.email ), E.mapLeft (e => [e])),
password : pipe (validatePassword (form.password ), E.mapLeft (e => [e])),
})
)
}
function SignupForm ( ) {
const [form, setForm] = useState ({ name : '' , email : '' , password : '' })
const [errors, setErrors] = useState<string []>([])
const handleSubmit = ( ) => {
pipe (
validateForm (form),
E.match (
(errs ) => setErrors (errs),
(valid ) => {
setErrors ([])
submitToServer (valid)
}
)
)
}
return (
<form onSubmit ={e => { e.preventDefault(); handleSubmit() }}>
<input
value ={form.name}
onChange ={e => setForm(f => ({ ...f, name: e.target.value }))}
placeholder="Name"
/>
<input
value ={form.email}
onChange ={e => setForm(f => ({ ...f, email: e.target.value }))}
placeholder="Email"
/>
<input
type ="password"
value ={form.password}
onChange ={e => setForm(f => ({ ...f, password: e.target.value }))}
placeholder="Password"
/>
{errors.length > 0 && (
<ul style ={{ color: 'red ' }}>
{errors.map((err, i) => <li key ={i} > {err}</li > )}
</ul >
)}
<button type ="submit" > Sign Up</button >
</form >
)
}
Field-Level Errors (Better UX) type FieldErrors = Partial <Record <keyof SignupForm , string >>
function validateFormWithFieldErrors (form : SignupForm ): E.Either <FieldErrors , ValidatedForm > {
const errors : FieldErrors = {}
pipe (validateName (form.name ), E.mapLeft (e => { errors.name = e }))
pipe (validateEmail (form.email ), E.mapLeft (e => { errors.email = e }))
pipe (validatePassword (form.password ), E.mapLeft (e => { errors.password = e }))
return Object .keys (errors).length > 0
? E.left (errors)
: E.right ({ name : form.name .trim (), email : form.email , password : form.password })
}
{errors.email && <span className ="error" > {errors.email}</span > }
3. Data Fetching with TaskEither TaskEither = async operation that might fail. Perfect for API calls.
Basic Fetch Hook import { useState, useEffect } from 'react'
import * as TE from 'fp-ts/TaskEither'
import * as E from 'fp-ts/Either'
import { pipe } from 'fp-ts/function'
const fetchJson = <T>(url : string ): TE .TaskEither <Error , T> =>
TE .tryCatch (
async () => {
const res = await fetch (url)
if (!res.ok ) throw new Error (`HTTP ${res.status} ` )
return res.json ()
},
(err ) => err instanceof Error ? err : new Error (String (err))
)
function useFetch<T>(url : string ) {
const [data, setData] = useState<T | null >(null )
const [error, setError] = useState<Error | null >(null )
const [loading, setLoading] = useState (true )
useEffect (() => {
setLoading (true )
setError (null )
pipe (
fetchJson<T>(url),
TE .match (
(err ) => {
setError (err)
setLoading (false )
},
(result ) => {
setData (result)
setLoading (false )
}
)
)()
}, [url])
return { data, error, loading }
}
function UserList ( ) {
const { data, error, loading } = useFetch<User []>('/api/users' )
if (loading) return <div > Loading...</div >
if (error) return <div > Error: {error.message}</div >
return (
<ul >
{data?.map(user => <li key ={user.id} > {user.name}</li > )}
</ul >
)
}
Chaining API Calls
const fetchUserWithPosts = (userId : string ) => pipe (
fetchJson<User >(`/api/users/${userId} ` ),
TE .flatMap (user => pipe (
fetchJson<Post []>(`/api/users/${userId} /posts` ),
TE .map (posts => ({ ...user, posts }))
))
)
Parallel API Calls import { sequenceT } from 'fp-ts/Apply'
const fetchDashboardData = ( ) => pipe (
sequenceT (TE .ApplyPar )(
fetchJson<User >('/api/user' ),
fetchJson<Stats >('/api/stats' ),
fetchJson<Notifications []>('/api/notifications' )
),
TE .map (([user, stats, notifications] ) => ({
user,
stats,
notifications
}))
)
4. RemoteData Pattern (The Right Way to Handle Async State) Stop using { data, loading, error } booleans. Use a proper state machine.
The Pattern
type RemoteData <E, A> =
| { _tag : 'NotAsked' }
| { _tag : 'Loading' }
| { _tag : 'Failure' ; error : E }
| { _tag : 'Success' ; data : A }
const notAsked = <E, A>(): RemoteData <E, A> => ({ _tag : 'NotAsked' })
const loading = <E, A>(): RemoteData <E, A> => ({ _tag : 'Loading' })
const failure = <E, A>(error : E): RemoteData <E, A> => ({ _tag : 'Failure' , error })
const success = <E, A>(data : A): RemoteData <E, A> => ({ _tag : 'Success' , data })
function fold<E, A, R>(
rd : RemoteData <E, A>,
onNotAsked : () => R,
onLoading : () => R,
onFailure : (e : E ) => R,
onSuccess : (a : A ) => R
): R {
switch (rd._tag ) {
case 'NotAsked' : return onNotAsked ()
case 'Loading' : return onLoading ()
case 'Failure' : return onFailure (rd.error )
case 'Success' : return onSuccess (rd.data )
}
}
Hook with RemoteData function useRemoteData<T>(fetchFn : () => Promise <T>) {
const [state, setState] = useState<RemoteData <Error , T>>(notAsked ())
const execute = async ( ) => {
setState (loading ())
try {
const data = await fetchFn ()
setState (success (data))
} catch (err) {
setState (failure (err instanceof Error ? err : new Error (String (err))))
}
}
return { state, execute }
}
function UserProfile ({ userId }: { userId: string } ) {
const { state, execute } = useRemoteData (() =>
fetch (`/api/users/${userId} ` ).then (r => r.json ())
)
useEffect (() => { execute () }, [userId])
return fold (
state,
() => <button onClick ={execute} > Load User</button > ,
() => <Spinner /> ,
(err ) => <ErrorMessage message ={err.message} onRetry ={execute} /> ,
(user ) => <UserCard user ={user} />
)
}
Why RemoteData Beats Booleans
interface BadState {
data : User | null
loading : boolean
error : Error | null
}
type GoodState = RemoteData <Error , User >
5. Referential Stability (Preventing Re-renders) fp-ts values like O.some(1) create new objects each render. React sees them as "changed".
The Problem
function BadComponent ( ) {
const [value, setValue] = useState (O.some (1 ))
useEffect (() => {
console .log ('value changed' )
}, [value])
}
Solution 1: useMemo
function GoodComponent ( ) {
const [rawValue, setRawValue] = useState<number | null >(1 )
const value = useMemo (
() => O.fromNullable (rawValue),
[rawValue]
)
useEffect (() => {
console .log ('value changed' )
}, [rawValue])
}
Solution 2: fp-ts-react-stable-hooks npm install fp-ts-react-stable-hooks
import { useStableO, useStableEffect } from 'fp-ts-react-stable-hooks'
import * as O from 'fp-ts/Option'
import * as Eq from 'fp-ts/Eq'
function StableComponent ( ) {
const [value, setValue] = useStableO (O.some (1 ))
useStableEffect (
() => { console .log ('value changed' ) },
[value],
Eq .tuple (O.getEq (Eq .eqNumber ))
)
}
6. Dependency Injection with Context Use ReaderTaskEither for testable components with injected dependencies.
Setup Dependencies import * as RTE from 'fp-ts/ReaderTaskEither'
import { pipe } from 'fp-ts/function'
import { createContext, useContext, ReactNode } from 'react'
interface AppDependencies {
api : {
getUser : (id : string ) => Promise <User >
updateUser : (id : string , data : Partial <User > ) => Promise <User >
}
analytics : {
track : (event : string , data ?: object ) => void
}
}
const DepsContext = createContext<AppDependencies | null >(null )
function AppProvider ({ deps, children }: { deps: AppDependencies; children: ReactNode } ) {
return <DepsContext.Provider value ={deps} > {children}</DepsContext.Provider >
}
function useDeps ( ): AppDependencies {
const deps = useContext (DepsContext )
if (!deps) throw new Error ('Missing AppProvider' )
return deps
}
Use in Components function UserProfile ({ userId }: { userId: string } ) {
const { api, analytics } = useDeps ()
const [user, setUser] = useState<RemoteData <Error , User >>(notAsked ())
useEffect (() => {
setUser (loading ())
api.getUser (userId)
.then (u => {
setUser (success (u))
analytics.track ('user_viewed' , { userId })
})
.catch (e => setUser (failure (e)))
}, [userId, api, analytics])
}
Testing with Mock Dependencies const mockDeps : AppDependencies = {
api : {
getUser : jest.fn ().mockResolvedValue ({ id : '1' , name : 'Test User' }),
updateUser : jest.fn ().mockResolvedValue ({ id : '1' , name : 'Updated' }),
},
analytics : {
track : jest.fn (),
},
}
test ('loads user on mount' , async () => {
render (
<AppProvider deps ={mockDeps} >
<UserProfile userId ="1" />
</AppProvider >
)
await screen.findByText ('Test User' )
expect (mockDeps.api .getUser ).toHaveBeenCalledWith ('1' )
})
7. React 19 Patterns
use() for Promises (React 19+) import { use, Suspense } from 'react'
function UserProfile ({ userPromise }: { userPromise: Promise <User> } ) {
const user = use (userPromise)
return <div > {user.name}</div >
}
function App ( ) {
const userPromise = fetchUser ('1' )
return (
<Suspense fallback ={ <Spinner /> }>
<UserProfile userPromise ={userPromise} />
</Suspense >
)
}
useActionState for Forms (React 19+) import { useActionState } from 'react'
import * as E from 'fp-ts/Either'
interface FormState {
errors : string []
success : boolean
}
async function submitForm (
prevState : FormState ,
formData : FormData
): Promise <FormState > {
const data = {
email : formData.get ('email' ) as string ,
password : formData.get ('password' ) as string ,
}
const result = pipe (
validateForm (data),
E.match (
(errors ) => ({ errors, success : false }),
async (valid) => {
await saveToServer (valid)
return { errors : [], success : true }
}
)
)
return result
}
function SignupForm ( ) {
const [state, formAction, isPending] = useActionState (submitForm, {
errors : [],
success : false
})
return (
<form action ={formAction} >
<input name ="email" type ="email" />
<input name ="password" type ="password" />
{state.errors.map(e => <p key ={e} className ="error" > {e}</p > )}
<button disabled ={isPending} >
{isPending ? 'Submitting...' : 'Sign Up'}
</button >
</form >
)
}
useOptimistic for Instant Feedback (React 19+) import { useOptimistic } from 'react'
function TodoList ({ todos }: { todos: Todo[] } ) {
const [optimisticTodos, addOptimisticTodo] = useOptimistic (
todos,
(state, newTodo : Todo ) => [...state, { ...newTodo, pending : true }]
)
const addTodo = async (text : string ) => {
const newTodo = { id : crypto.randomUUID (), text, done : false }
addOptimisticTodo (newTodo)
await saveTodo (newTodo)
}
return (
<ul >
{optimisticTodos.map(todo => (
<li key ={todo.id} style ={{ opacity: todo.pending ? 0.5 : 1 }}>
{todo.text}
</li >
))}
</ul >
)
}
8. Common Patterns Cheat Sheet
Render Based on Option
pipe (
maybeUser,
O.match (
() => <LoginButton /> ,
(user ) => <UserMenu user ={user} />
)
)
O.fold (
() => <LoginButton /> ,
(user ) => <UserMenu user ={user} />
)(maybeUser)
const name = pipe (
maybeUser,
O.map (u => u.name ),
O.getOrElse (() => 'Guest' )
)
Render Based on Either pipe (
validationResult,
E.match (
(errors ) => <ErrorList errors ={errors} /> ,
(data ) => <SuccessMessage data ={data} />
)
)
Safe Array Rendering import * as A from 'fp-ts/Array'
const firstUser = pipe (
users,
A.head ,
O.map (user => <Featured user ={user} /> ),
O.getOrElse (() => <NoFeaturedUser /> )
)
const adminUser = pipe (
users,
A.findFirst (u => u.role === 'admin' ),
O.map (admin => <AdminBadge user ={admin} /> ),
O.toNullable
)
Conditional Props
const modalProps = {
isOpen : true ,
...pipe (
maybeTitle,
O.map (title => ({ title })),
O.getOrElse (() => ({}))
)
}
When to Use What Situation Use Value might not exist Option<T>Operation might fail (sync) Either<E, A>Async operation might fail TaskEither<E, A>Need loading/error/success UI RemoteData<E, A>Form with multiple validations Either with validation applicativeDependency injection Context + ReaderTaskEither Prevent re-renders with fp-ts useMemo or fp-ts-react-stable-hooks
Libraries
Connections
Domain: [[Software Entwicklung]]
Kategorie: [[Frontend Entwicklung]]
Navigation: [[Skills Uebersicht]], [[Home]]