Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

Create a performant distributed context state for React by composing reusable state logic.

License

NotificationsYou must be signed in to change notification settings

lukasbach/synergies

Repository files navigation

Create a performant distributed context state for React and compose reusable state logic.

TestingPrettyStorybook DeploymentNPM VersionNPM TypingsMinzipped sizeTreeshaking ReportPackagephobia Report

Find out more atsynergies.js.org!

synergies is a tiny (~3kB), yet powerful state management library for React. It allows you tospecify small state atoms, that you can combine into Synergies of multiple atoms that define shared state logic.Features include

  • Distributed state: You can inject individual atoms at multiple arbitrary points in your component hierarchy.This allows you to both define unique global state logic in your application root, and smaller reusable componentsthat have their own context-based state. Your state logic can not only read and write to the atomsprovided by the closest provider, but to all atoms provided upwards in the component hierarchy.
  • Immutable update logic:synergies usesimmer to provide drafts of your state in your update handlers,so you can more easily update state.
  • Performant update-triggers: Even though a synergy provider can provide several atoms and can access any atomsfrom other providers upwards the hierarchy, calling update logic will only trigger updates on componentsthat read from atoms that were actually changed. Againimmer is used to let you update drafts of your state,and smartly detects which atoms were actually changed and which ones were only read from during the update.
  • Asynchronous update logic: No more thunk plugins! Update handlers can be asynchronous, and you can evenmanually trigger updates on certain atoms while still being in the middle of the update handler.
  • Reusable state logic: Since you can provide atom state rather low down in the hierarchy, you can reuse smallpieces of state between components, while still maintaining more global parts of your app state farther upin the component hierarchy.
  • Typed: Full type safety for everything.
  • Tiny package: 3kB + immer. Use it for your global app state, or just replace small context providers withsynergies to simplify your codebase and speed up your state.

Product Hunt

Usage

Get started by installing the library:

yarn add synergies immer

Create your first state atoms:

constvalueAtom=createAtom("");constisInitialStateAtom=createAtom(true);

Synergyze your atoms to create React Hooks:

constuseSetValue=createSynergy(valueAtom,isInitialStateAtom).createAction((newValue:string)=>(valueDraft,isInitialStateDraft)=>{// Components that read from the value atom will be updated.valueDraft.current=newValue;if(isInitialStateDraft.current){// If isInitialState is already false, then the draft will not be updated,// and components that read from it will not trigger a rerender.isInitialStateDraft.current=false;}});// Every atom is also a synergy of itself, so we can call `createSelector` also on atoms.constuseValue=valueAtom.createSelector(value=>value);constuseIsInitialState=isInitialStateAtom.useValue;// shortcut for directly reading atom state

Provide your atoms:

<SynergyProvideratoms={[valueAtom,isInitialStateAtom]}>{/* Components that consume value and isInitialState... */}{/* We can also nest other synergy providers */}<SynergyProvideratoms={[moreLocalizedAtom]}>{/* Can read from and write to all three atoms. */}</SynergyProvider>{/* Reuse providers with more localized state */}<SynergyProvideratoms={[moreLocalizedAtom]}>{/* ... */}</SynergyProvider></SynergyProvider>

Use your hooks:

constComponent=()=>{constsetValue=useSetValue();constvalue=useValue();return(<inputvalue={value}onChange={e=>setValue(e.target.value)}/>)}

You can find more examples and details atsynergies.js.org!

More advanced examples

Async update logic

synergies supports asynchronous update actions. You can also trigger atomsin the middle of an update handler, so that their subscribers get rerendered beforethe action has completed.

This is shown in the following example, where an API fetch call is dispatched in aaction handler. TheisLoading atom is updated to true immediately when the fetchis dispatched, while the update call continues to load the data from the server.Components reading theisLoading atom will be rerendered immediately. Once the datahas loaded, we update thedata atom with the fetched result, and update theisLoadingatom is updated to false, triggering rerenders of all components that read from eitherthedata or theisLoading atom.

Note that, if theisLoading atom would not rerender a second time at the end, onlycomponents subscribing to thedata atom would be rerendered.

constisLoadingAtom=createAtom(false);constdataAtom=createAtom(null);// Async update handlerconstuseFetchData=createSynergy(dataAtom,isLoadingAtom).createAction(()=>async(data,isLoading)=>{isLoading.current=true;// Trigger rerenders of all components that read from the `isLoading` atom.// The `isLoading` draft will be discarded, so we need to use the new one// that the `trigger` method returns.isLoading=isLoading.trigger();constres=awaitfetch("https://pokeapi.co/api/v2/pokemon/pikachu");// Update the `data` and `isLoading` atomsdata.current=awaitres.json();isLoading.current=false;});// For simplicity, read from both atoms at onceconstusePokemonData=createSynergy(dataAtom,isLoadingAtom).createSelector((data,isLoading)=>({ data, isLoading}));exportconstExample=()=>{const{ data, isLoading}=usePokemonData();constfetchData=useFetchData();constresetData=useReset();return!data&&!isLoading ?(<ButtononClick={fetchData}>LoadPokemon</Button>) :isLoading ?(<div>Loading...</div>) :(<div>{data.name}hasaheightof{data.height}andtheabilities{" "}{data.abilities.map(({ ability})=>ability.name).join(", ")}</div>);};

Nested synergy providers

// ---------------------// Atoms// ---------------------constfilterAtom=createAtom(false);constitemsAtom=createAtom([{todo:"First Todo",checked:true},{todo:"Second Todo",checked:false},{todo:"Third Todo",checked:false},]);constinputValueAtom=createAtom("");// ---------------------// Selectors and state actions// ---------------------constuseFilteredItems=createSynergy(itemsAtom,filterAtom).createSelector((items,filter)=>items.filter(({ checked})=>!filter||checked));constuseAddTodo=createSynergy(itemsAtom,inputValueAtom).createAction(()=>(items,input)=>{items.current.push({todo:input.current,checked:false});input.current="";});constuseToggleTodo=itemsAtom.createAction((id:number)=>items=>{items.current[id].checked=!items.current[id].checked;});constuseToggleFilter=filterAtom.createAction(()=>filter=>{filter.current=!filter.current;});// ---------------------// Components that use the hooks// ---------------------constList=()=>{constitems=useFilteredItems();consttoggle=useToggleTodo();return(<>{items.map((item,index)=>(<Checkboxkey={index}checked={item.checked}label={item.todo}onChange={()=>toggle(index)}/>))}</>);};constTodoInput=()=>{const[value]=inputValueAtom.useValue();constsetValue=inputValueAtom.useSet();constaddTodo=useAddTodo();return(<ControlGroup><InputGroupplaceholder="Add a todo"value={value}onChange={e=>setValue(e.target.value)}/><ButtononClick={addTodo}>Add</Button></ControlGroup>);};constFilterButton=()=>{consttoggle=useToggleFilter();const[isToggled]=filterAtom.useValue();return(<ButtononClick={toggle}active={isToggled}>Onlyshowcompletedtodos</Button>);};// ---------------------// App container// ---------------------exportconstApp=()=>(// We don't have to nest the providers so extremely, but this demonstrates how you can inject// atoms at any place in the hierarchy and they can still communicate upwards with other// atoms.<SynergyProvideratoms={[filterAtom]}><FilterButton/><SynergyProvideratoms={[itemsAtom]}><List/><SynergyProvideratoms={[inputValueAtom]}><TodoInput/></SynergyProvider></SynergyProvider></SynergyProvider>);

Maintenance

When developing locally, run in the root directory...

  • yarn to install dependencies
  • yarn test to run tests in all packages
  • yarn build to build distributables and typings inpackages/{package}/out
  • yarn storybook to run a local storybook server
  • yarn build-storybook to build the storybook
  • npx lerna version to interactively bump thepackages versions. This automatically commits the version, tags the commit and pushes to git remote.
  • npx lerna publish to publish all packagesto NPM that have changed since the last release. This automatically bumps the versions interactively.

[8]ページ先頭

©2009-2025 Movatter.jp